1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00
freebsd/usr.sbin/ypbind/ypbind.c

1023 lines
25 KiB
C
Raw Normal View History

/*
* Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/fcntl.h>
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
#include <sys/stat.h>
#include <sys/uio.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_rmt.h>
#include <rpc/rpc_com.h>
#include <rpcsvc/yp.h>
#include <rpcsvc/ypclnt.h>
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
#include "yp_ping.h"
#ifndef BINDINGDIR
#define BINDINGDIR "/var/yp/binding"
#endif
#ifndef YPBINDLOCK
#define YPBINDLOCK "/var/run/ypbind.lock"
#endif
struct _dom_binding {
struct _dom_binding *dom_pnext;
char dom_domain[YPMAXDOMAIN + 1];
struct sockaddr_in dom_server_addr;
long int dom_vers;
int dom_lockfd;
int dom_alive;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
int dom_broadcast_pid;
int dom_pipe_fds[2];
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
int dom_default;
};
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
#define READFD ypdb->dom_pipe_fds[0]
#define WRITEFD ypdb->dom_pipe_fds[1]
#define BROADFD broad_domain->dom_pipe_fds[1]
extern bool_t xdr_domainname(), xdr_ypbind_resp();
extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
extern bool_t xdr_ypbind_setdom();
void checkwork(void);
void *ypbindproc_null_2_yp(SVCXPRT *, void *, CLIENT *);
void *ypbindproc_setdom_2_yp(SVCXPRT *, struct ypbind_setdom *, CLIENT *);
void rpc_received(char *, struct sockaddr_in *, int);
void broadcast(struct _dom_binding *);
int ping(struct _dom_binding *);
int tell_parent(char *, struct sockaddr_in *);
void handle_children(struct _dom_binding *);
void reaper(int);
void terminate(int);
void yp_restricted_mode(char *);
int verify(struct in_addr);
static char *domain_name;
static struct _dom_binding *ypbindlist;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
static struct _dom_binding *broad_domain;
#define YPSET_NO 0
#define YPSET_LOCAL 1
#define YPSET_ALL 2
static int ypsetmode = YPSET_NO;
static int ypsecuremode = 0;
static int ppid;
#define NOT_RESPONDING_HYSTERESIS 10
static int not_responding_count = 0;
/*
* Special restricted mode variables: when in restricted mode, only the
* specified restricted_domain will be bound, and only the servers listed
* in restricted_addrs will be used for binding.
*/
#define RESTRICTED_SERVERS 10
static int yp_restricted = 0;
static int yp_manycast = 0;
static struct in_addr restricted_addrs[RESTRICTED_SERVERS];
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
/* No more than MAX_CHILDREN child broadcasters at a time. */
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
#ifndef MAX_CHILDREN
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
#define MAX_CHILDREN 5
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
#endif
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
/* No more than MAX_DOMAINS simultaneous domains */
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
#ifndef MAX_DOMAINS
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
#define MAX_DOMAINS 200
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
#endif
/* RPC timeout value */
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
#ifndef FAIL_THRESHOLD
#define FAIL_THRESHOLD 20
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
#endif
/* Number of times to fish for a response froma particular set of hosts */
#ifndef MAX_RETRIES
#define MAX_RETRIES 30
#endif
static int retries = 0;
static int children = 0;
static int domains = 0;
static int yplockfd;
static fd_set fdsr;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
static SVCXPRT *udptransp, *tcptransp;
void *
ypbindproc_null_2_yp(SVCXPRT *transp, void *argp, CLIENT *clnt)
{
static char res;
bzero(&res, sizeof(res));
return &res;
}
static struct ypbind_resp *
ypbindproc_domain_2_yp(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
{
static struct ypbind_resp res;
struct _dom_binding *ypdb;
char path[MAXPATHLEN];
bzero(&res, sizeof res);
res.ypbind_status = YPBIND_FAIL_VAL;
res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
if (strchr(*argp, '/')) {
syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
rejecting.", *argp);
return(&res);
}
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
if (strcmp(ypdb->dom_domain, *argp) == 0)
break;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
if (ypdb == NULL) {
if (yp_restricted) {
syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp);
return (&res);
}
if (domains >= MAX_DOMAINS) {
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
syslog(LOG_WARNING, "domain limit (%d) exceeded",
MAX_DOMAINS);
res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
return (&res);
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
}
if (strlen(*argp) > YPMAXDOMAIN) {
syslog(LOG_WARNING, "domain %s too long", *argp);
res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
return (&res);
}
ypdb = malloc(sizeof *ypdb);
1995-05-30 03:57:47 +00:00
if (ypdb == NULL) {
syslog(LOG_WARNING, "malloc: %m");
res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
return (&res);
}
bzero(ypdb, sizeof *ypdb);
strlcpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain);
ypdb->dom_vers = YPVERS;
ypdb->dom_alive = 0;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
ypdb->dom_default = 0;
ypdb->dom_lockfd = -1;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
unlink(path);
ypdb->dom_pnext = ypbindlist;
ypbindlist = ypdb;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
domains++;
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (ping(ypdb)) {
return (&res);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
res.ypbind_status = YPBIND_SUCC_VAL;
res.ypbind_resp_u.ypbind_error = 0; /* Success */
memcpy(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
&ypdb->dom_server_addr.sin_addr.s_addr, sizeof(u_int32_t));
memcpy(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
&ypdb->dom_server_addr.sin_port, sizeof(u_short));
/*printf("domain %s at %s/%d\n", ypdb->dom_domain,
inet_ntoa(ypdb->dom_server_addr.sin_addr),
ntohs(ypdb->dom_server_addr.sin_port));*/
return (&res);
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
void *
ypbindproc_setdom_2_yp(SVCXPRT *transp, ypbind_setdom *argp, CLIENT *clnt)
{
struct sockaddr_in *fromsin, bindsin;
static char *result = NULL;
if (strchr(argp->ypsetdom_domain, '/')) {
syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
rejecting.", argp->ypsetdom_domain);
return(NULL);
}
fromsin = svc_getcaller(transp);
switch (ypsetmode) {
case YPSET_LOCAL:
if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
svcerr_noprog(transp);
return(NULL);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
break;
case YPSET_ALL:
break;
case YPSET_NO:
default:
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
svcerr_noprog(transp);
return(NULL);
}
if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
svcerr_noprog(transp);
return(NULL);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
if (argp->ypsetdom_vers != YPVERS) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
svcerr_noprog(transp);
return(NULL);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
bzero(&bindsin, sizeof bindsin);
bindsin.sin_family = AF_INET;
memcpy(&bindsin.sin_addr.s_addr,
&argp->ypsetdom_binding.ypbind_binding_addr,
sizeof(u_int32_t));
memcpy(&bindsin.sin_port,
&argp->ypsetdom_binding.ypbind_binding_port,
sizeof(u_short));
rpc_received(argp->ypsetdom_domain, &bindsin, 1);
return((void *) &result);
}
void
ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
domainname ypbindproc_domain_2_arg;
struct ypbind_setdom ypbindproc_setdom_2_arg;
} argument;
struct authunix_parms *creds;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
char *(*local)();
switch (rqstp->rq_proc) {
case YPBINDPROC_NULL:
xdr_argument = xdr_void;
xdr_result = xdr_void;
local = (char *(*)()) ypbindproc_null_2_yp;
break;
case YPBINDPROC_DOMAIN:
xdr_argument = xdr_domainname;
xdr_result = xdr_ypbind_resp;
local = (char *(*)()) ypbindproc_domain_2_yp;
break;
case YPBINDPROC_SETDOM:
switch (rqstp->rq_cred.oa_flavor) {
case AUTH_UNIX:
creds = (struct authunix_parms *)rqstp->rq_clntcred;
if (creds->aup_uid != 0) {
svcerr_auth(transp, AUTH_BADCRED);
return;
}
break;
default:
svcerr_auth(transp, AUTH_TOOWEAK);
return;
}
xdr_argument = xdr_ypbind_setdom;
xdr_result = xdr_void;
local = (char *(*)()) ypbindproc_setdom_2_yp;
break;
default:
svcerr_noproc(transp);
return;
}
bzero(&argument, sizeof(argument));
if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
svcerr_decode(transp);
return;
}
result = (*local)(transp, &argument, rqstp);
if (result != NULL &&
!svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
svcerr_systemerr(transp);
}
return;
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
/* Jack the reaper */
void
reaper(int sig)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
{
int st;
while (wait3(&st, WNOHANG, NULL) > 0)
children--;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
void
terminate(int sig)
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
{
struct _dom_binding *ypdb;
char path[MAXPATHLEN];
if (ppid != getpid())
exit(0);
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
close(ypdb->dom_lockfd);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (ypdb->dom_broadcast_pid)
kill(ypdb->dom_broadcast_pid, SIGINT);
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
unlink(path);
}
close(yplockfd);
unlink(YPBINDLOCK);
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
pmap_unset(YPBINDPROG, YPBINDVERS);
exit(0);
}
1995-05-30 03:57:47 +00:00
int
main(int argc, char *argv[])
{
struct timeval tv;
int i;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
DIR *dird;
struct dirent *dirp;
struct _dom_binding *ypdb, *next;
/* Check that another ypbind isn't already running. */
if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
err(1, "%s", YPBINDLOCK);
if (flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
errx(1, "another ypbind is already running. Aborting");
/* XXX domainname will be overridden if we use restricted mode */
yp_get_default_domain(&domain_name);
if (domain_name[0] == '\0')
errx(1, "domainname not set. Aborting");
for (i = 1; i<argc; i++) {
if (strcmp("-ypset", argv[i]) == 0)
ypsetmode = YPSET_ALL;
else if (strcmp("-ypsetme", argv[i]) == 0)
ypsetmode = YPSET_LOCAL;
else if (strcmp("-s", argv[i]) == 0)
ypsecuremode++;
else if (strcmp("-S", argv[i]) == 0 && argc > i)
yp_restricted_mode(argv[++i]);
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
else if (strcmp("-m", argv[i]) == 0)
yp_manycast++;
else
errx(1, "unknown option: %s", argv[i]);
}
if (strlen(domain_name) > YPMAXDOMAIN)
warnx("truncating domain name %s", domain_name);
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
/* blow away everything in BINDINGDIR (if it exists) */
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
if ((dird = opendir(BINDINGDIR)) != NULL) {
char path[MAXPATHLEN];
while ((dirp = readdir(dird)) != NULL)
if (strcmp(dirp->d_name, ".") &&
strcmp(dirp->d_name, "..")) {
sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
unlink(path);
}
closedir(dird);
}
#ifdef DAEMON
if (daemon(0,0))
err(1, "fork");
#endif
pmap_unset(YPBINDPROG, YPBINDVERS);
udptransp = svcudp_create(RPC_ANYSOCK);
if (udptransp == NULL)
errx(1, "cannot create udp service");
if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_UDP))
errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)");
tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (tcptransp == NULL)
errx(1, "cannot create tcp service");
if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_TCP))
errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)");
/* build initial domain binding, make it "unsuccessful" */
ypbindlist = malloc(sizeof *ypbindlist);
if (ypbindlist == NULL)
errx(1, "malloc");
bzero(ypbindlist, sizeof *ypbindlist);
strlcpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain);
ypbindlist->dom_vers = YPVERS;
ypbindlist->dom_alive = 0;
ypbindlist->dom_lockfd = -1;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
ypbindlist->dom_default = 1;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
domains++;
signal(SIGCHLD, reaper);
signal(SIGTERM, terminate);
ppid = getpid(); /* Remember who we are. */
openlog(argv[0], LOG_PID, LOG_DAEMON);
if (madvise(NULL, 0, MADV_PROTECT) != 0)
syslog(LOG_WARNING, "madvise(): %m");
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
/* Kick off the default domain */
broadcast(ypbindlist);
while (1) {
fdsr = svc_fdset;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
tv.tv_sec = 60;
tv.tv_usec = 0;
switch (select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
case 0:
checkwork();
break;
case -1:
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (errno != EINTR)
syslog(LOG_WARNING, "select: %m");
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
break;
default:
for (ypdb = ypbindlist; ypdb; ypdb = next) {
next = ypdb->dom_pnext;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (READFD > 0 && FD_ISSET(READFD, &fdsr)) {
handle_children(ypdb);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (children == (MAX_CHILDREN - 1))
checkwork();
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
}
svc_getreqset(&fdsr);
break;
}
}
/* NOTREACHED */
exit(1);
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
void
checkwork(void)
{
struct _dom_binding *ypdb;
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
ping(ypdb);
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
/* The clnt_broadcast() callback mechanism sucks. */
/*
* Receive results from broadcaster. Don't worry about passing
* bogus info to rpc_received() -- it can handle it. Note that we
* must be sure to invalidate the dom_pipe_fds descriptors here:
* since descriptors can be re-used, we have to make sure we
* don't mistake one of the RPC descriptors for one of the pipes.
* What's weird is that forgetting to invalidate the pipe descriptors
* doesn't always result in an error (otherwise I would have caught
* the mistake much sooner), even though logically it should.
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
*/
void
handle_children(struct _dom_binding *ypdb)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
{
char buf[YPMAXDOMAIN + 1];
struct sockaddr_in addr;
int d = 0, a = 0;
struct _dom_binding *y, *prev = NULL;
char path[MAXPATHLEN];
if ((d = read(READFD, &buf, sizeof(buf))) <= 0)
syslog(LOG_WARNING, "could not read from child: %m");
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0)
syslog(LOG_WARNING, "could not read from child: %m");
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(READFD);
FD_CLR(READFD, &fdsr);
FD_CLR(READFD, &svc_fdset);
READFD = WRITEFD = -1;
if (d > 0 && a > 0)
rpc_received(buf, &addr, 0);
else {
for (y = ypbindlist; y; y = y->dom_pnext) {
if (y == ypdb)
break;
prev = y;
}
switch (ypdb->dom_default) {
case 0:
if (prev == NULL)
ypbindlist = y->dom_pnext;
else
prev->dom_pnext = y->dom_pnext;
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, YPVERS);
close(ypdb->dom_lockfd);
unlink(path);
free(ypdb);
domains--;
return;
case 1:
ypdb->dom_broadcast_pid = 0;
ypdb->dom_alive = 0;
broadcast(ypdb);
return;
default:
break;
}
}
return;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
/*
* Send our dying words back to our parent before we perish.
*/
int
tell_parent(char *dom, struct sockaddr_in *addr)
{
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
char buf[YPMAXDOMAIN + 1];
struct timeval timeout;
fd_set fds;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
timeout.tv_sec = 5;
timeout.tv_usec = 0;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
sprintf(buf, "%s", broad_domain->dom_domain);
if (write(BROADFD, &buf, sizeof(buf)) < 0)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return(1);
/*
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
* Stay in sync with parent: wait for it to read our first
* message before sending the second.
*/
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
FD_ZERO(&fds);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
FD_SET(BROADFD, &fds);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1)
return(1);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (FD_ISSET(BROADFD, &fds)) {
if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return(1);
} else {
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return(1);
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(BROADFD);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return (0);
}
static bool_t
broadcast_result(bool_t *out, struct sockaddr_in *addr)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
{
if (retries >= MAX_RETRIES) {
bzero(addr, sizeof(struct sockaddr_in));
if (tell_parent(broad_domain->dom_domain, addr))
syslog(LOG_WARNING, "lost connection to parent");
return (TRUE);
}
if (yp_restricted && verify(addr->sin_addr)) {
retries++;
syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr));
return (FALSE);
} else {
if (tell_parent(broad_domain->dom_domain, addr))
syslog(LOG_WARNING, "lost connection to parent");
return (TRUE);
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
/*
* The right way to send RPC broadcasts.
* Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast()
* blocks while waiting for replies, so we have to fork off separate
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
* broadcaster processes that do the waiting and then transmit their
* results back to the parent for processing. We also have to remember
* to save the name of the domain we're trying to bind in a global
* variable since clnt_broadcast() provides no way to pass things to
* the 'eachresult' callback function.
*/
void
broadcast(struct _dom_binding *ypdb)
{
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
bool_t out = FALSE;
enum clnt_stat stat;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (pipe(ypdb->dom_pipe_fds) < 0) {
syslog(LOG_WARNING, "pipe: %m");
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return;
}
if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr) {
if (not_responding_count++ >= NOT_RESPONDING_HYSTERESIS) {
not_responding_count = NOT_RESPONDING_HYSTERESIS;
syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding",
inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
}
}
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
broad_domain = ypdb;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
flock(ypdb->dom_lockfd, LOCK_UN);
switch ((ypdb->dom_broadcast_pid = fork())) {
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
case 0:
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(READFD);
signal(SIGCHLD, SIG_DFL);
signal(SIGTERM, SIG_DFL);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
break;
case -1:
syslog(LOG_WARNING, "fork: %m");
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(READFD);
close(WRITEFD);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return;
default:
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(WRITEFD);
FD_SET(READFD, &svc_fdset);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
children++;
return;
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
/* Release all locks before doing anything else. */
while (ypbindlist) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
close(ypbindlist->dom_lockfd);
ypbindlist = ypbindlist->dom_pnext;
}
close(yplockfd);
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
/*
* Special 'many-cast' behavior. If we're in restricted mode,
* we have a list of possible server addresses to try. What
* we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK
* procedure and time the replies. Whoever replies fastest
* gets to be our server. Note that this is not a broadcast
* operation: we transmit uni-cast datagrams only.
*/
if (yp_restricted && yp_manycast) {
short port;
int i;
struct sockaddr_in sin;
i = __yp_ping(restricted_addrs, yp_restricted,
ypdb->dom_domain, &port);
if (i == -1) {
bzero(&ypdb->dom_server_addr,
sizeof(struct sockaddr_in));
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
if (tell_parent(ypdb->dom_domain,
&ypdb->dom_server_addr))
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
syslog(LOG_WARNING, "lost connection to parent");
} else {
bzero(&sin, sizeof(struct sockaddr_in));
bcopy(&restricted_addrs[i],
&sin.sin_addr, sizeof(struct in_addr));
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
sin.sin_family = AF_INET;
sin.sin_port = port;
if (tell_parent(broad_domain->dom_domain, &sin))
syslog(LOG_WARNING,
"lost connection to parent");
}
_exit(0);
}
retries = 0;
{
char *ptr;
ptr = ypdb->dom_domain;
stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
(xdrproc_t)xdr_domainname, &ptr,
(xdrproc_t)xdr_bool, &out,
Bring in a hybrid of SunSoft's transport-independent RPC (TI-RPC) and associated changes that had to happen to make this possible as well as bugs fixed along the way. Bring in required TLI library routines to support this. Since we don't support TLI we've essentially copied what NetBSD has done, adding a thin layer to emulate direct the TLI calls into BSD socket calls. This is mostly from Sun's tirpc release that was made in 1994, however some fixes were backported from the 1999 release (supposedly only made available after this porting effort was underway). The submitter has agreed to continue on and bring us up to the 1999 release. Several key features are introduced with this update: Client calls are thread safe. (1999 code has server side thread safe) Updated, a more modern interface. Many userland updates were done to bring the code up to par with the recent RPC API. There is an update to the pthreads library, a function pthread_main_np() was added to emulate a function of Sun's threads library. While we're at it, bring in NetBSD's lockd, it's been far too long of a wait. New rpcbind(8) replaces portmap(8) (supporting communication over an authenticated Unix-domain socket, and by default only allowing set and unset requests over that channel). It's much more secure than the old portmapper. Umount(8), mountd(8), mount_nfs(8), nfsd(8) have also been upgraded to support TI-RPC and to support IPV6. Umount(8) is also fixed to unmount pathnames longer than 80 chars, which are currently truncated by the Kernel statfs structure. Submitted by: Martin Blapp <mb@imp.ch> Manpage review: ru Secure RPC implemented by: wpaul
2001-03-19 12:50:13 +00:00
(resultproc_t)broadcast_result);
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
if (stat != RPC_SUCCESS) {
bzero(&ypdb->dom_server_addr,
sizeof(struct sockaddr_in));
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr))
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
syslog(LOG_WARNING, "lost connection to parent");
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
_exit(0);
}
/*
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
* The right way to check if a server is alive.
* Attempt to get a client handle pointing to the server and send a
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
* YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE,
* we invalidate this binding entry and send out a broadcast to try to
* establish a new binding. Note that we treat non-default domains
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
* specially: once bound, we keep tabs on our server, but if it
* goes away and fails to respond after one round of broadcasting, we
* abandon it until a client specifically references it again. We make
* every effort to keep our default domain bound, however, since we
* need it to keep the system on its feet.
*/
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
int
ping(struct _dom_binding *ypdb)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
{
bool_t out;
struct timeval interval, timeout;
enum clnt_stat stat;
int rpcsock = RPC_ANYSOCK;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
CLIENT *client_handle;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
interval.tv_sec = FAIL_THRESHOLD;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
interval.tv_usec = 0;
timeout.tv_sec = FAIL_THRESHOLD;
timeout.tv_usec = 0;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (ypdb->dom_broadcast_pid)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return(1);
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr,
YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE,
RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
/* Can't get a handle: we're dead. */
ypdb->dom_alive = 0;
ypdb->dom_vers = -1;
broadcast(ypdb);
return(1);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
{
char *ptr;
ptr = ypdb->dom_domain;
stat = clnt_call(client_handle, YPPROC_DOMAIN,
(xdrproc_t)xdr_domainname, &ptr,
(xdrproc_t)xdr_bool, &out, timeout);
if (stat != RPC_SUCCESS || out == FALSE) {
ypdb->dom_alive = 0;
ypdb->dom_vers = -1;
clnt_destroy(client_handle);
broadcast(ypdb);
return(1);
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
}
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
clnt_destroy(client_handle);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
return(0);
}
void
rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
{
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
struct _dom_binding *ypdb, *prev = NULL;
struct iovec iov[2];
struct ypbind_resp ybr;
char path[MAXPATHLEN];
int fd;
/*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr),
ntohs(raddrp->sin_port), dom);*/
if (dom == NULL)
return;
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
if (strcmp(ypdb->dom_domain, dom) == 0)
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
break;
Performace improvements/simplifications/cleanups: - Make the child process reaper signal-driven. (Previously, we called reaper() once a second each time we went through the select() loop. This was convenient, but inefficient.) - Increase main select() timeout from 1 second to 60 seconds and use this as the ping timer instead of using timestamps in the _dom_binding structure. This nd the reaper() change noted above makes ypbind a little less CPU-intensive. - Don't flag EINTR's from select() as errors since they will happen as a result of incoming SIGCHLD's interrupting select(). - Prevent possible resource hogging. Currently we malloc() memory each time a user process asks us to establish a binding for a domain, but we never free it. This could lead to serious memory leakage if a 'clever' user did something like ask ypwhich to check the bindings for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive. (This would also make a mess out of the /var/yp/binding directory.) We now avoid this silliness by a) limiting the maximum number of simultaneous bindings we can manage to 200, and b) free()ing _dom_binding structures of secondary domains whose servers have stopped responding. We unlink the /var/yp/binding/domain.vers files for the free()ed domains too. (This is safe to do since a client can prod us into reestablishing the binding, at which time we'll simply allocate a new _dom_binding structure for it.) We keep count of the total number of domains. If asked to allocate more than the maximum, we return an error. I have yet to hear of anybody needing 200 simultaneous NIS bindings, so this should be enough. (I chose the number 200 arbitrarily. It can be increased if need be.) - Changed "server not responding"/"server OK" messages to display server IP addresses again since it looks spiffier. - Use daemon() to daemonify ourselves, - Added a SIGTERM handler that removes all binding files and unregisters the ypbind service from the portmapper when a SIGTERM in received. - The comment 'blow away everything in BINDINGDIR' has no associated code. Give it some: clean out /var/yp/binding at startup (if it exists). This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to go poking around in here anymore. (Of course, this means I can start working on my ypserv whishlist now... :)
1995-05-10 23:02:41 +00:00
prev = ypdb;
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
if (ypdb && force) {
if (ypdb->dom_broadcast_pid) {
kill(ypdb->dom_broadcast_pid, SIGINT);
close(READFD);
FD_CLR(READFD, &fdsr);
FD_CLR(READFD, &svc_fdset);
READFD = WRITEFD = -1;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
}
}
/* if in secure mode, check originating port number */
if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) {
syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.",
inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port),
dom);
if (ypdb != NULL) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
ypdb->dom_broadcast_pid = 0;
ypdb->dom_alive = 0;
}
return;
}
if (raddrp->sin_addr.s_addr == (long)0) {
switch (ypdb->dom_default) {
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
case 0:
if (prev == NULL)
ypbindlist = ypdb->dom_pnext;
else
prev->dom_pnext = ypdb->dom_pnext;
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, YPVERS);
close(ypdb->dom_lockfd);
unlink(path);
free(ypdb);
domains--;
return;
case 1:
ypdb->dom_broadcast_pid = 0;
ypdb->dom_alive = 0;
broadcast(ypdb);
return;
default:
break;
}
}
if (ypdb == NULL) {
if (force == 0)
return;
if (strlen(dom) > YPMAXDOMAIN) {
syslog(LOG_WARNING, "domain %s too long", dom);
return;
}
ypdb = malloc(sizeof *ypdb);
1995-05-30 03:57:47 +00:00
if (ypdb == NULL) {
syslog(LOG_WARNING, "malloc: %m");
return;
}
bzero(ypdb, sizeof *ypdb);
strlcpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
ypdb->dom_lockfd = -1;
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
ypdb->dom_default = 0;
ypdb->dom_pnext = ypbindlist;
ypbindlist = ypdb;
}
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
/* We've recovered from a crash: inform the world. */
if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr) {
if (not_responding_count >= NOT_RESPONDING_HYSTERESIS) {
not_responding_count = 0;
syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK",
inet_ntoa(raddrp->sin_addr), ypdb->dom_domain);
}
}
bcopy(raddrp, &ypdb->dom_server_addr,
sizeof ypdb->dom_server_addr);
ypdb->dom_vers = YPVERS;
ypdb->dom_alive = 1;
Reviewed by: rgrimes, jkh and davidg (sort of) Rod, Jordan and David have more or less given me the OK on this with the understanding that it doesn't change any functionality. It doesn't: these are bug fixes only. No other part of the system should be affected. Of course, since I'm the only one working on NIS, you'll just have to take my word on it. :) Fixes for the following annoyingly subtle bugs: - ypbindproc_setdom_2 is supposed to be declared void *, not boot_t *, and it fails to correctly signal failures back to the ypset(8) command: we need to call one of the svcerr_*() functions (in this case, svcerr_noprog() seems a logical choice -- we're really cheating a bit here because nothing else quite fits) to tell ypset that the attempt to set the binding for a domain failed. If we don't do this, failed ypset attempts either appear (incorrectly) to succeed, or they time out. - The lock handling for child processes isn't quite right. The child broadcaster processes have to release all locks on the binding files and the ypbind.lock file. - The parent ypbind process will SEGV if you do the following: -- start ypbind with the -ypset or -ypsetme flag -- type 'ypwhich -d random_unserved_domain' -- type 'ypset -d random_unserved_domain anyhost' -- type 'ypwhich -d random_unserved_domain' again -- wait about 60 seconds What happens is this: the ypwhich command causes ypbind to fork a broadcaster process that searches for a server for random_unserved_domain. If you then use ypset to force a binding while this process is still alive, the state flags that tell the ypbind parent process that the child is running will be cleared. The second ypwhich command then causes a *second* child process to be forked for random_unserved_domain, which is verbotten. When the first broadcaster exits and tells the parent that it wasn't able to find a server for the domain, the parent clobbers the entry for random_unserved_domain. Then the second broadcaster exits and the same thing happens, only trying to clobber the entry twice causes a SEGV. The fix for this is a slight change in program structure: since we can't have more than one broadcaster for a given domain at a time, we save the pipe descriptors and pid for the child broadcaster in members of the _dom_binding struct for the domain. (As a side effect, we can get rid of the global child_fds variable.) So when rpc_received() finds that it's been asked to do a ypset for a domain for which a broadcaster process exists, it sends a SIGINT to the child to kill it and closes the pipe to the now-dead child. This keeps everything in sync and insures that we don't leak file descriptors. - ping() should be using YPPROC_DOMAIN rather than YPPROC_DOMAIN_NONACK when it does its clnt_call() to the server. - Removed the check for client_handle == NULL in ping() and make client_handle local to ping instead of a member of the _dom_binding struct. This fixes another potential ypset problem: using ypset to force a binding to a machine that has an NIS server but which *doesn't* support the domain we're after can result in permanently bogus bindings. - the 'server OK' message prints the wrong IP address.
1995-05-26 05:28:00 +00:00
ypdb->dom_broadcast_pid = 0;
if (ypdb->dom_lockfd != -1)
close(ypdb->dom_lockfd);
ypbind.c: Major overhaul. - Moved to a more client-driven model. We aggressively attempt to keep the default domain bound (as before) but we give up on non-default domains if we lose contact with a server and fail to get a response after one round of broadcasting. This helps drastically reduce the amount of network bandwitdh that ypbind consumes: if a client references the secondary domain at some later point, this will prod ypbind into establishing a new binding anyway, so continuously broadcasting without need is pointless. Note that we still actively seek out a binding for our default domain even if no client program has queried us yet. I'm not exactly sure if this matches SunOS's behavior or not, but I decided to do it this way since we can get into all sorts of trouble if our default domain comes unbound. Even so, we're still much quieter than we used to be. - Removed a bunch of no-longer pertinent comments and a couple of chunks of #ifdef 0'ed code that no longer fit in to the new layout. - Theo deRaadt must have become frustrated with the callback mechanism in clnt_broadcast(), because he shamelessly stole the clnt_broadcast() code right out of the RPC library and hacked it up to suit his needs. (Comments and all! :) I can understand why: clnt_broadcast() blocks while awaiting replies. Changing this behavior requires surgery. However, you can work around this: fork the broadcast into a child process and relay the results back to the parent via a pipe. (Careful obervation has shown that the SunOS ypbind forks children for broadcasting too, though I can only guess what sort of interprocess communication it uses. pipe() seems to do the job well enough.) This may seem like the long way around, but it's not really that hard to implement, and I'd prefer to use documented RPC library functions wherever possible. We're careful to limit the number of simultaneous broadcasters to avoid swamping the system (the current limit is 5). Each clnt_broadcast() call only sends out a small number of packets at increasing intervals. We're also careful not to spawn more than one bradcaster for a given domain. - Used clntudp_bufcreate() and clnt_call() to implement a ping() function for directly querying a particular server so that we can check if it's still alive. This lets me completely remove the old bradcasting code and use actual RPC library calls instead, at the cost of more than a few handfulls of torn-out hair. (Make no mistake folks: I *HATE* RPC.) Currently, the ping interval is one minute. - Fixed another potential 'nfds too big for select()' bug: use _rpc_dtablesize() instead of getdtablesize(). - Quieted gcc -Wall a bit. - Probably a bunch of other stuff that I've forgotten. ypbind.8: - Updated man page to reflect modifications. ypwhich.c: - Small mind-o fix from last time: decode error results from ypbind correctly (*groan*) yplib.c: - same as above - Change behavior of _yp_dobind() a little: if we get back a 'Domain not bound' error for a given domain, retry a few times before giving up and passing the error back to the caller. We have to sleep for a few seconds between tries since the 'Domain not bound' error comes back immediately (by repeatedly looping, we end up pounding on ypbind). We retry at most 20 times at 5 second intervals. This gives us a full minute to get a response. This seems to deviate a bit from SunOS behavior -- it appears to wait forever -- but I don't like the idea of perpetually hanging inside a library call. Note that this should fix the problems some people have with bindings not being established fast enough at boot time; sometimes amd is started in /etc/rc after ypbind has run but before it gets a binding set up. The automounter gets annoyed at this and tends to exit. By pausing ther YP calls until a binding is ready, we avoid this situation. - Another _yp_dobind() change: if we determine that our binding files are unlocked or nonexistent, jump directly to code that pokes ypbind into restablishing the binding. Again, if it fails, we'll time out eventually and return.
1995-04-26 19:03:16 +00:00
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
#ifdef O_SHLOCK
if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
(void)mkdir(BINDINGDIR, 0755);
if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
return;
}
#else
if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
(void)mkdir(BINDINGDIR, 0755);
if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
return;
}
flock(fd, LOCK_SH);
#endif
/*
* ok, if BINDINGDIR exists, and we can create the binding file,
* then write to it..
*/
ypdb->dom_lockfd = fd;
iov[0].iov_base = (char *)&(udptransp->xp_port);
iov[0].iov_len = sizeof udptransp->xp_port;
iov[1].iov_base = (char *)&ybr;
iov[1].iov_len = sizeof ybr;
bzero(&ybr, sizeof ybr);
ybr.ypbind_status = YPBIND_SUCC_VAL;
memcpy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
&raddrp->sin_addr.s_addr, sizeof(u_int32_t));
memcpy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
&raddrp->sin_port, sizeof(u_short));
if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
syslog(LOG_WARNING, "write: %m");
close(ypdb->dom_lockfd);
ypdb->dom_lockfd = -1;
return;
}
}
/*
* Check address against list of allowed servers. Return 0 if okay,
* 1 if not matched.
*/
int
verify(struct in_addr addr)
{
int i;
for (i = 0; i < RESTRICTED_SERVERS; i++)
if (!bcmp(&addr, &restricted_addrs[i], sizeof(struct in_addr)))
return(0);
return(1);
}
/*
* Try to set restricted mode. We default to normal mode if we can't
* resolve the specified hostnames.
*/
void
yp_restricted_mode(char *args)
{
struct hostent *h;
int i = 0;
char *s;
/* Find the restricted domain. */
if ((s = strsep(&args, ",")) == NULL)
return;
domain_name = s;
/* Get the addresses of the servers. */
while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) {
if ((h = gethostbyname(s)) == NULL)
return;
bcopy (h->h_addr_list[0], &restricted_addrs[i],
sizeof(struct in_addr));
i++;
}
/* ypset and ypsetme not allowed with restricted mode */
ypsetmode = YPSET_NO;
This commit adds support to ypbind(8) for binding to non-local servers. The standard SunOS ypbind(8) (and, until now, the FreeBSD ypbind) only selects servers based on whether or not they respond to clnt_broadcast(). Ypbind(8) broadcasts to the YPPROC_DOMAIN_NONACK procedure and waits for answers; whichever server answers first is the one ypbind uses for the local client binding. This mechanism fails when binding across subnets is desired. In order for a client on one subnet to bind to a server on another subnet, the gateway(s) between the client and server must be configured to forward broadcasts. If this is not possible, then a slave server must be installed on the remote subnet. If this is also not possible, you have to force the client to bind to the remote server with ypset(8). Unfortunately, this last option is less than ideal. If the remote server becomes unavailable, ypbind(8) will lose its binding and revert to its broadcast-based search behavior. Even if there are other servers available, or even if the original server comes back up, ypbind(8) will not be able to create a new binding since all the servers are on remote subnets where its broadcasts won't be heard. If the administrator isn't around to run ypset(8) again, the system is hosed. In some Linux NIS implementations, there exists a yp.conf file where you can explicitly specify a server address and avoid the use of ypbind altogether. This is not desireable since it removes the possibility of binding to an alternate server in the event that the one specified in yp.conf crashes. Some people have mentioned to me how they though the 'restricted mode' operation (using the -S flag) could be used as a solution for this problem since it allows one to specify a list of servers. In fact, this is not the case: the -S flag just tells ypbind(8) that when it listens for replies to its broadcasts, it should only honor them if the replying hosts appear in the specified restricted list. This behavior has now been changed. If you use the -m flag in conjunction with the -S flag, ypbind(8) will use a 'many-cast' instead of a broadcast for choosing a server. In many-cast mode, ypbind(8) will transmit directly to the YPPROC_DOMAIN_NONACK procedure of all the servers specified in the restricted mode list and then wait for a reply. As with the broadcast method, whichever server from the list answers first is used for the local binding. All other behavior is the same: ypbind(8) continues to ping its bound server every 60 seconds to insure it's still alive and will many-cast again if the server fails to respond. The code used to achieve this is in yp_ping.c; it includes a couple of modified RPC library routines. Note that it is not possible to use this mechanism without using the restricted list since we need to know the addresses of the available NIS servers ahead of time in order to transmit to them. Most-recently-requested by: Tom Samplonius
1997-05-25 19:49:33 +00:00
yp_restricted = i;
return;
}