static char rcsid[] = "$Id: slave.c,v 1.14 1997/07/04 13:43:06 swen Exp $" ;

#include <config.h>
#include <sys/types.h>
#include <unistd.h> /* setsid */
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h> /* getenv, exit */
#include <stdarg.h>
#include <string.h> /* strcmp */
#include <ctype.h>
#include <memory.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include <rpc/pmap_clnt.h> /* for pmap_unset */
#include "ourhdr.h"
#include "ypbind.h"
#if HAVE_IPC
#if HAVE_SYS_IPC_H
#include <sys/ipc.h>
#endif
#if HAVE_SYS_SEM_H
#include <sys/sem.h>
#endif
#if HAVE_SYS_SHM_H
#include <sys/shm.h>
#endif
#endif /* HAVE_IPC */

#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
#endif

extern SVCXPRT *udptransp;
extern domainname mydomain;
extern int broken_server;
extern int debug;
#if HAVE_IPC
extern int use_ipc;
#endif
#if HAVE_MMAP
extern int use_mmap;
#endif
extern unsigned int ping_interval;
extern char **Argv;
extern int Argc;

extern CLIENT * clntudp_create(struct sockaddr_in *, u_long, u_long,
                               struct timeval, int *);

struct binding *ypbindlist;
int semid = -1;
int lockfd;
domainname askdomain;
volatile int hangup;
static void check_domain(struct binding *);
static void bind_domain(struct binding *);
#ifdef BINDINGDIR
static void update_bindingfile(struct binding *);
static void close_bindingfile(struct binding *);
#endif

pid_t
start_slave(void)
{
  struct sigaction sact;
  pid_t pid;
  
  pid = fork();
  if (pid < 0)
    log_sys("fork failed");
  if (0 != pid) /* parent */
    return pid;
  
  inststr(Argv, Argc, "ypbind (slave)");

  sigemptyset(&sact.sa_mask);
  sact.sa_handler = toggle_debug;
  sact.sa_flags = SA_RESTART;
  if (0 != sigaction(SIGUSR1, &sact, NULL))
    log_ret("Could not install signal handler for SIGUSR1");
  sact.sa_handler = handle_hangup;
  if (0 != sigaction(SIGHUP, &sact, NULL))
    log_ret("Could not install signal handler for SIGHUP");
  
  for (;;)
    {
      hangup = 0;
      init_binding();
      
      while (!hangup)
        {
          if (0 != ping_interval)
            {
              check_binding();
              sleep(ping_interval);
            }
          else
            sleep(30); /* wait for hangup */
        }
    }
}

void
init_binding(void)
{
  struct binding ypdb;

  write_lock_binding();
  memset(ypbindlist, 0, sizeof(struct binding) * _MAXDOMAIN);
#if HAVE_MMAP
  if (!use_mmap)
#endif
    {
      lseek(lockfd, 0, SEEK_SET);
      if (writen(lockfd, ypbindlist, sizeof(struct binding) * _MAXDOMAIN) <= 0)
        log_sys("cannot write ypbindlist");
    }
  un_lock_write_binding();

  memset((void *)&ypdb, 0, sizeof(struct binding));
  strncpy(ypdb.domain, mydomain, YPMAXDOMAIN);
  ypdb.is_bound = TRUE; /* always bind to our NIS domain */
  ypdb.active = 0;
  ypdb.lockfd = -1;
  ypdb.server[0].client_handle = NULL;
  ypdb.is_alive = FALSE;
  ypdb.server[0].use_broadcast = TRUE; /* default is using broadcast */
  update_entry(&ypdb);
  parse_config_file(_PATH_YPCONF);
  return;
}

void
check_binding(void)
{
  struct binding ypdb;
  time_t t;
  int domain;
  
  time(&t);
  /* Check all bound domains */
  for (domain = 0; domain < _MAXDOMAIN; domain++)
    {
      read_lock_binding();
      memcpy(&ypdb, &ypbindlist[domain], sizeof ypdb);
      un_lock_read_binding();
      if (ypdb.is_bound)
        {
          /* Check if binding is still valid */
          check_domain(&ypdb);
          if (!ypdb.is_alive)
            /* Oops, no valid binding */
            {
	      close_bindingfile(&ypdb);
              /* Try to bind the domain */
              bind_domain(&ypdb);
            }
        }
    }
}

void
add_server(char *dom, struct sockaddr_in *raddrp, CLIENT *clnt_handlep,
           const char *host, bool_t use_broadcast)
{
  struct binding entry;
  int active;
  
  if (NULL == dom || (0 != get_entry(dom, &entry)))
    return;

  if (!entry.is_bound)
    {
      entry.active = 0;
      entry.lockfd = -1;
      memset((void *)&entry.server, 0, sizeof(struct bound_server));
      entry.is_alive = FALSE;
    }

  /* find empty slot */
  for (active = 0; active < _MAXSERVER; active++)
    if (!entry.server[active].filled) break;
  
  /* FIXME: should we ignore add_server, when there are no more slots? */

  active = active % _MAXSERVER;
  if (use_broadcast) active =  _MAXSERVER - 1;

  if (debug)
    log_msg("add_server() domain: %s, host: %s, %sbroadcast, slot: %d",
            dom, host ? host : "unknown", use_broadcast ? "" : "no",
            active);
  
  if (NULL != raddrp)
    {
      if (!broken_server &&
          (ntohs(raddrp->sin_port) >= IPPORT_RESERVED ||
           ntohs(raddrp->sin_port) <  IPPORT_RESERVED/2))
        {
          log_msg("Answer from %s on illegal port", inet_ntoa(raddrp->sin_addr));
          return;
        }
      memcpy(&entry.server[active].server_addr, &raddrp->sin_addr,
             sizeof entry.server[active].server_addr);
      entry.server[active].server_port = raddrp->sin_port;
      entry.is_alive = TRUE;
    }
  if (NULL != entry.server[active].client_handle)
    clnt_destroy(entry.server[active].client_handle);
  entry.server[active].client_handle = clnt_handlep;
  if (NULL != host)
    {
      if (entry.server[active].host) free(entry.server[active].host);
      entry.server[active].host = strdup(host);
    }
  entry.server[active].version = YPVERS;
  entry.server[active].use_broadcast = use_broadcast;
  entry.server[active].filled = TRUE;
  entry.active = active;
  
  update_entry(&entry);
  return;
}

void
bindto_server(char *ypdomain, char *server)
{
  struct sockaddr_in server_addr;
  int sock;
  bool_t out;
  enum clnt_stat status;
  struct timeval timeout;
  CLIENT *clnt_handlep = NULL;
  int i = 0;
  
  struct hostent *host;

  if (debug)
    log_msg("bindto_server: domain %s, host %s", ypdomain, server);
  
  
  host = res_gethostbyname(server);
  if (NULL == host)
    {
      switch (h_errno)
        {
        case HOST_NOT_FOUND:
          log_msg("Unknown host: %s", server);
          break;
        case TRY_AGAIN:
          log_msg("Host name lookup failure");
          break;
        case NO_DATA:
          log_msg("No address associated with name: %s", server);
          break;
        case NO_RECOVERY:
          log_msg("Unknown server error");
          break;
        default:
          log_ret("gethostbyname: Unknown error");
          break;
        }
      return;
    }
  
  memset((char *)&server_addr, 0, sizeof server_addr);
  server_addr.sin_family = host->h_addrtype;
  server_addr.sin_port = htons(0);
  sock = RPC_ANYSOCK;
  while(NULL != host->h_addr_list[i])
    {
      memcpy(&server_addr.sin_addr, host->h_addr_list[i],
             host->h_length);
      
      timeout.tv_sec = 1;
      timeout.tv_usec = 0;
      clnt_handlep = clntudp_create(&server_addr, YPPROG, YPVERS,
                                    timeout, &sock);
      if (NULL != clnt_handlep)
        break;
      i++;
    }
  
  if (NULL == clnt_handlep)
    {
      log_msg("clnt_create for server %s failed", host->h_name);
      return; 
    }
  
  timeout.tv_sec = 5;
  timeout.tv_usec = 0;
  status = clnt_call(clnt_handlep, YPPROC_DOMAIN,
                     (xdrproc_t) xdr_domainname_ypbind, ypdomain,
                     (xdrproc_t) xdr_bool, (caddr_t)&out,
                     timeout);
  if (RPC_SUCCESS != status)
    {
      log_msg(clnt_sperror(clnt_handlep, host->h_name));
      clnt_destroy(clnt_handlep);
    }
  else if (TRUE != out)
    {
      log_msg("domain %s not served by %s", ypdomain, host->h_name);
      clnt_destroy(clnt_handlep);
    }
  else
    add_server(ypdomain, &server_addr, clnt_handlep, host->h_name, FALSE);
  
  return;
}

void
ping_server(struct binding *ypdb, int active)
{
  int status;
  bool_t out;
  struct timeval timeout;
  
  if (NULL != ypdb->server[active].client_handle)
    {
      timeout.tv_sec = 2;
      timeout.tv_usec = 0;
      status = clnt_call(ypdb->server[active].client_handle, YPPROC_DOMAIN,
                         (xdrproc_t) xdr_domainname_ypbind, ypdb->domain,
                         (xdrproc_t) xdr_bool, (caddr_t)&out,
                         timeout);
      
      if ((RPC_SUCCESS != status) || (TRUE != out))
        {
          RPC_SUCCESS != status
            ? log_msg(clnt_sperror(ypdb->server[active].client_handle,
                                   ypdb->server[active].host ? ypdb->server[active].host : "unknown"))
            : log_msg("domain %s not served by %s",
                      ypdb->domain, ypdb->server[active].host ? ypdb->server[active].host : "unknown") ;
          clnt_destroy(ypdb->server[active].client_handle);
          ypdb->server[active].client_handle = NULL;
          ypdb->is_alive = FALSE;
        }
      else
        ypdb->is_alive=TRUE;
    }
  return;
}

bool_t
eachresult(bool_t *out, struct sockaddr_in *addr)
{
  CLIENT *clnt_handlep;
  struct timeval timeout;
  int sock;
  
  if (*out)
    {
      if(debug)
        {
          struct hostent *hostentp;
          hostentp = gethostbyaddr((char *) &addr->sin_addr.s_addr,
                                   sizeof(addr->sin_addr.s_addr), AF_INET);
          log_msg("Answer from server %s .", hostentp->h_name);
        }
      
      sock = RPC_ANYSOCK;
      timeout.tv_sec = 1;
      timeout.tv_usec = 0;
      clnt_handlep = clntudp_create(addr, YPPROG, YPVERS, timeout, &sock);
      add_server(askdomain, addr, clnt_handlep, NULL, TRUE);
      return 1;
    }
  else
    {
      return 0;
    }
}

void
broadcast(struct binding *ypdb)
{
  bool_t out;
  enum clnt_stat  status;
      /* update global variable for eachresult */
  askdomain = ypdb->domain;
  status = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
                          (xdrproc_t) xdr_domainname_ypbind, askdomain,
                          (xdrproc_t) xdr_bool, (void *)&out,
                          (resultproc_t) eachresult);
  if (RPC_SUCCESS != status)
    {
#ifdef BINDINGDIR
      close_bindingfile(ypdb);
#endif
      log_msg("broadcast: %s.", clnt_sperrno(status));
    }
}


/*
 * Routines for parsing the config file ( /etc/yp.conf )
 *
 */

void
parse_config_file(const char *path)
      /* parse the config file, check bindings */
{
  FILE *fp;
  char buf[1024];
  char *cp, *tmp;
  char tmpserver[81], tmpdomain[YPMAXDOMAIN + 1];
  int count;
  int found_my_domain=0; /* set to 1 when an entry for the
                          * local domain is found */
  
  fp = fopen(path, "r");
  if (NULL == fp) /* use broadcast, when yp.conf is not found */
    {
      if (debug)
        log_msg("config file unreadable, "
                "using broadcast for domain %s", mydomain);
      add_server(mydomain, NULL, NULL, NULL, TRUE);
      return;
    }

  if (debug)
    log_msg("parsing config file");

  while ((cp = fgets(buf, sizeof(buf), fp)) != NULL)
    {
      tmp = strchr(cp, '#');  /* Kommentare ausblenden */
      if (tmp)
        *tmp = '\0';
      while (isspace(*cp))    /* Leerraum ueberlesen */
        cp++;
      if (*cp == '\0')        /* Leerzeile ignorieren */
        continue;
      
      if (debug)
        log_msg("Trying entry: %s", cp);
      count = sscanf(cp, "domain %64s server %80s", tmpdomain, tmpserver);
      if (2 == count)
        {
          if (debug)
            log_msg("parsed domain %s server %s", tmpdomain, tmpserver);
          if (0 == strcmp(mydomain, tmpdomain))
            {
              bindto_server(tmpdomain, tmpserver);
              found_my_domain++;
            }
          else
            add_server(tmpdomain, NULL, NULL, tmpserver, FALSE);
          continue;
        }
      count = sscanf(cp, "domain %s broadcast", tmpdomain);
      if (1 == count)
        {
          if (debug)
            log_msg("parsed domain %s broadcast", tmpdomain);
          add_server(tmpdomain, NULL, NULL, NULL, TRUE);
          if (0 == strcmp(mydomain, tmpdomain))
            found_my_domain++;
          continue;
        }
      count = sscanf(cp, "ypserver %80s", tmpserver);
      if (1 == count)
        {
          if (debug)
            log_msg("parsed ypserver %s", tmpserver);
          bindto_server(mydomain, tmpserver);
          found_my_domain++;
          continue;
        }
    }
  fclose(fp);
  if (!found_my_domain)
    {
      if (debug)
        log_msg("no entry for the local domain %s, "
                "using broadcast", mydomain);
      add_server(mydomain, NULL, NULL, NULL, TRUE);
    }    
  return;
}

#if HAVE_MMAP
static struct binding *
mmap_shared_anon(size_t len)
{
#ifdef MAP_ANON
  struct binding * result;
  result = (struct binding *) mmap(0, len, (PROT_READ | PROT_WRITE), 
                                   (MAP_ANON | MAP_SHARED), -1, 0);
  return result;
#else
  return MAP_FAILED;
#endif
}

static struct binding *
mmap_shared_dev_zero(size_t len)
{
  struct binding * result = MAP_FAILED;
#ifdef MAP_ANON
  int fd;
  
  fd = open("/dev/zero", O_RDWR);
  if (-1 == fd)
    result = MAP_FAILED;
  else
    result = (struct binding *) mmap(0, len, (PROT_READ | PROT_WRITE), 
                                     (MAP_ANON | MAP_SHARED), fd, 0);
#endif
  return result;
}

static struct binding *
mmap_shared_file(size_t len, int filedes)
{
  struct binding * result;
  result = (struct binding *) mmap(0, len, (PROT_READ | PROT_WRITE), 
                                   MAP_SHARED, filedes, 0);
  return result;
}


static struct binding *
mmap_shared(size_t len, int filedes)
{
  struct binding * result;
  /* Linux has no shared anonymous mapping yet, but may get it later,
   * so work around this */
  result = mmap_shared_anon(len);
  if (MAP_FAILED == result)
    result = mmap_shared_dev_zero(len);
  if (MAP_FAILED == result)
    result = mmap_shared_file(len, filedes);
  
  return result;
}
#endif /* HAVE_MMAP */

void
init_master_slave_communication(void)
{
  int shmid;
  struct shmid_ds dummy;
  ushort empty[2] = {0,0};

#if HAVE_IPC
  if (use_ipc)
    {
      if ((semid = semget(IPC_PRIVATE, 2, IPC_CREAT|SHM_R|SHM_W)) < 0)
        log_sys("cannot create semaphore");
      
      
      if ((shmid = shmget(IPC_PRIVATE, 
                          sizeof(struct binding) * _MAXDOMAIN,
                          IPC_CREAT|SHM_R|SHM_W)) < 0)
        log_sys("cannot create shared memory segment");
      
      ypbindlist = (struct binding *)shmat(shmid, NULL, 0);
      
      /* mark it for deletion just in case we are not able to
       * handle it on our own. handles case of failed attachment
       * as well */
      (void)shmctl(shmid, IPC_RMID, &dummy);  
      
      if (!ypbindlist)
        log_sys("cannot attach to shared memory segment");
      
      memset(ypbindlist, 0, sizeof(struct binding) * _MAXDOMAIN);
      
      semctl(semid, 0, SETALL, empty);
    }
  else
#endif /* HAVE_IPC */
    {
      lockfd = open_lockfile();
#if HAVE_MMAP
      if (use_mmap)
        {
          ypbindlist = mmap_shared(sizeof(struct binding) * _MAXDOMAIN, lockfd);
          if ((caddr_t) -1 == (caddr_t) ypbindlist)
            log_sys("cannot create shared region");
        }
      else
#endif /* HAVE_MMAP */
        {
          ypbindlist = (struct binding *) calloc(_MAXDOMAIN, sizeof(struct binding));
          if (NULL == ypbindlist)
            log_sys("cannot create shared region");
          if (writen(lockfd, ypbindlist, sizeof(struct binding) * _MAXDOMAIN) <= 0)
            log_sys("cannot write shared region");
        }
    }
}

#if HAVE_IPC
void
terminate_master_slave_communication(void)
{
  if(semid != -1)
  {
    (void)semctl(semid, 0, IPC_RMID, NULL);
    semid = -1;
  }
}

#define IPC_LOCK_RETRY 5
#endif /* HAVE_IPC */

void 
read_lock_binding(void)
{
  int i, status;
#if HAVE_IPC
  struct sembuf sops[2];
  
  if (use_ipc)
    {
      sops[0].sem_num = 1; /* check if write semaphore is clear */
      sops[0].sem_op = 0;
      sops[0].sem_flg = IPC_NOWAIT;
      sops[1].sem_num = 0; /* then signal reading */
      sops[1].sem_op = 1;
      sops[1].sem_flg = SEM_UNDO|IPC_NOWAIT;
      
      for(i=0; i<IPC_LOCK_RETRY; i++)
        {
          if (!semop(semid, sops, sizeof(sops)/sizeof(struct sembuf)))
            break;
          sleep(1);
        }
      if(i==IPC_LOCK_RETRY)
        log_sys("cannot create read lock");
    }
  else
#endif /* HAVE_IPC */
    {
      status = lock_reg(lockfd, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0);
      if (0 != status) 
        log_sys("set read lock");
#if HAVE_MMAP
      if (!use_mmap)
#endif
        {
          lseek(lockfd, 0, SEEK_SET);
          if (readn(lockfd, ypbindlist, sizeof(struct binding) * _MAXDOMAIN) < 0)
            log_sys("cannot read ypbindlist");
        }
    }
  return;
}

void 
write_lock_binding(void)
{
  int i, status;
#if HAVE_IPC
  struct sembuf sops[3];
      
  if (use_ipc)
    {
      sops[0].sem_num = 0; /* check if read semaphore is clear */
      sops[0].sem_op = 0;
      sops[0].sem_flg = IPC_NOWAIT;
      sops[1].sem_num = 1; /* check if write semaphore is clear */
      sops[1].sem_op = 0;
      sops[1].sem_flg = IPC_NOWAIT;
      sops[2].sem_num = 1; /* then signal writing */
      sops[2].sem_op = 1;
      sops[2].sem_flg = SEM_UNDO|IPC_NOWAIT;
      
      for(i=0; i<IPC_LOCK_RETRY; i++)
        {
          if (!semop(semid, sops, sizeof(sops)/sizeof(struct sembuf)))
            break;
          sleep(1);
        }
      if(i==IPC_LOCK_RETRY)
        log_sys("cannot create write lock");
    }
  else
#endif /* HAVE_IPC */
    {
      status = lock_reg(lockfd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0); 
      if (0 != status) 
        log_sys("set write lock");
    }
  return;
}


void
un_lock_read_binding(void)
{
  int i;
#if HAVE_IPC
  struct sembuf sops[1];
  
  if (use_ipc)
    {
      sops[0].sem_num = 0; /* clear read semaphore */
      sops[0].sem_op = -1;
      sops[0].sem_flg = SEM_UNDO|IPC_NOWAIT;
      
      for(i=0; i<IPC_LOCK_RETRY; i++)
        {
          if (!semop(semid, sops, sizeof(sops)/sizeof(struct sembuf)))
            break;
          sleep(1);
        }
      if(i==IPC_LOCK_RETRY)
        log_sys("error unlocking");
    }
  else
#endif /* HAVE_IPC */
    un_lock_write_binding();
  return;
}

void 
un_lock_write_binding(void)
{
  int i, status;
#if HAVE_IPC
  struct sembuf sops[1];
  
  if (use_ipc)
    {
      sops[0].sem_num = 1; /* clear write semaphore */
      sops[0].sem_op = -1;
      sops[0].sem_flg = SEM_UNDO|IPC_NOWAIT;
      
      for(i=0; i<IPC_LOCK_RETRY; i++)
        {
          if (!semop(semid, sops, sizeof(sops)/sizeof(struct sembuf)))
            break;
          sleep(1);
        }
      if(i==IPC_LOCK_RETRY)
        log_sys("error unlocking");
    }
  else
#endif /* HAVE_IPC */
    {
      status = lock_reg(lockfd, F_SETLKW, F_UNLCK, 0, SEEK_SET, 0);
      if (0 != status)
        log_sys("unlock");
    }
  return;
}


int
open_lockfile(void)
{
  char name[L_tmpnam];
  int fd;
  
  if (NULL == tmpnam(name))
    log_sys("tmpnam failed");
  
  fd = open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd < 0)
    log_sys("cannot open lockfile");
  /* Need to extend the file for mmap + fcntl */
  if (0 != ftruncate(fd, sizeof(struct binding) * _MAXDOMAIN))
    log_sys("ftruncate");
  /* remove the file, but keep it open, so we have an invisible
   * lockfile. Note: This does not work with NFS-mounted directories
   * on some systems. */
  unlink(name);
  return fd;
}

int
get_entry(char *dom, struct binding *entry)
{
  struct binding *ypdb = NULL;
  
  ypdb = find_entry(dom);
  if (NULL == ypdb)
    return -1;
  read_lock_binding();
  memcpy(entry, ypdb, sizeof (struct binding));
  un_lock_read_binding();
  return 0;
}

void
update_entry(struct binding *entry)
{
  /* update entry in ypbindlist. Lock while updating */
  struct binding *ypdb = NULL;

  ypdb = find_entry(entry->domain);
  if (NULL == ypdb)
    return;
  write_lock_binding();
  memcpy(ypdb, entry, sizeof (struct binding));
#if HAVE_MMAP
  if (!use_mmap)
#endif
    {
      lseek(lockfd, 0, SEEK_SET);
      if (writen(lockfd, ypbindlist, sizeof(struct binding) * _MAXDOMAIN) <= 0)
        log_sys("cannot write ypbindlist");
    }
  un_lock_write_binding();
#ifdef BINDINGDIR
  if (entry->is_alive)
    update_bindingfile(entry);
#endif
  if (debug)
    log_msg("%s entry for domain %s: server %s, port %d",
            entry->is_alive ? "updated" : "cleared", entry->domain,
            inet_ntoa(entry->server[entry->active].server_addr),
            ntohs(entry->server[entry->active].server_port));
  return;
}

struct binding *
find_entry(char *dom)
{
  int i;
  struct binding *ypdb = NULL;
  
  read_lock_binding();
      /* Find entry for domain dom */
  for (i = 0; i < _MAXDOMAIN; i++)
    {
      if (ypbindlist[i].is_bound && 0 == strcmp(ypbindlist[i].domain, dom))
        {
          ypdb = &ypbindlist[i];
          break;
        }
    }
  
  if (NULL == ypdb) /* no entry for domain dom */
    {
          /* find empty slot */
      for (i = 0; i < _MAXDOMAIN; i++)
        {
          if (FALSE == ypbindlist[i].is_bound)
            {
              ypdb = &ypbindlist[i];
              break;
            }
        }
    }
  un_lock_read_binding();
  return ypdb;
}

RETSIGTYPE
handle_hangup(int sig)
{
  log_msg("rereading config file");

  hangup = 1;

  return;
}

static void
check_domain(struct binding *ypdb)
{
  if (ypdb->is_alive)
    {
      int i, active;
      /* Check all servers (from /etc/yp.conf) */
      for (i = 0; i < _MAXSERVER; i++)
        {
          active = (ypdb->active + i) % _MAXSERVER;
          if (!ypdb->server[active].filled) continue;
          if (debug)
            log_msg("pinging server %s, port %d",
                    inet_ntoa(ypdb->server[active].server_addr),
                    ntohs(ypdb->server[active].server_port));
          /* mark it unbound and check if alive */
          ypdb->is_alive = FALSE;
          ping_server(ypdb, active);
          if (!ypdb->is_alive)
            update_entry(ypdb); /* entry has changed */
          else
            break; /* ok, break for-loop */
        }
      /* We have a server. Check if it is the same as before */
      if (ypdb->is_alive
          && active != ypdb->active
          && ypdb->server[active].filled)
        {
          ypdb->active=active;
          update_entry(ypdb); 
        }
    }
}

static void
bind_domain(struct binding *ypdb)
{
  int active;

  for (active = 0; active < _MAXSERVER; active++)
    {
      if (!ypdb->server[active].filled) continue;
      if (ypdb->server[active].use_broadcast)
        /* Try broadcast until server is found.
         * Since the domain is not bound, we will get here
         * on the next iteration
         */
        {
          if (debug)
            log_msg("broadcasting for domain %s", ypdb->domain);
          broadcast(ypdb);
        }
      else
        {
          if (debug)
            log_msg("binding to server %s",
                    ypdb->server[active].host);
          bindto_server(ypdb->domain, ypdb->server[active].host);
        }
      if (ypdb->is_alive)
        {
          ypdb->active = active;
          break;
        }
    }
}

#ifdef BINDINGDIR
static void
update_bindingfile(struct binding *entry)
{
  struct iovec iov[2];
  struct ypbind_resp ybr;
  pid_t lockpid;
  char path[MAXPATHLEN];
  int fd, len, status;

  if (-1 != entry->lockfd)
    close(entry->lockfd);
  sprintf(path, "%s/%s.%ld", BINDINGDIR,
          entry->domain, entry->server[entry->active].version);
  if ((fd = open(path, O_CREAT | O_RDWR | O_TRUNC, FILE_MODE )) == -1)
    {
      if (-1 == mkdir(BINDINGDIR, DIR_MODE))
        log_ret("mkdir");
      if ((fd = open(path, O_CREAT | O_RDWR | O_TRUNC, FILE_MODE)) == -1)
        return;
    }
  
  lockpid = lock_test(fd, F_RDLCK, 0, SEEK_SET, 0);
  if (0 != lockpid) 
    log_quit("%s already locked by pid %d", path, lockpid);
  
  status = read_lock(fd, 0, SEEK_SET, 0);
  if (0 != status) 
    log_sys("set lock");
      
  /* ok, if BINDINGDIR exists, and we can create the binding file, then
   * write to it.. */
  entry->lockfd = fd;
  
  iov[0].iov_base = (caddr_t) &(udptransp->xp_port);
  iov[0].iov_len = sizeof udptransp->xp_port;
  iov[1].iov_base = (caddr_t) &ybr;
  iov[1].iov_len = sizeof ybr;
  
  memset(&ybr, 0, sizeof ybr);
  ybr.ypbind_status = YPBIND_SUCC_VAL;
  ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr = entry->server[entry->active].server_addr;
  ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port = entry->server[entry->active].server_port;
  
  len = iov[0].iov_len + iov[1].iov_len ;
  if (writev(entry->lockfd, iov, 2) != len )
    {
      log_ret("writev");
      close(entry->lockfd);
      (void) unlink(path);
      entry->lockfd = -1;
    }
}

static void
close_bindingfile(struct binding *ypdb)
{
  char path[MAXPATHLEN];
  
  if ( -1 != ypdb->lockfd)
    {
      close(ypdb->lockfd);
      ypdb->lockfd = -1;
      sprintf(path, "%s/%s.%ld", BINDINGDIR,
              ypdb->domain, ypdb->server[ypdb->active].version);
      unlink(path);
    }
}
#endif

/*
 * $Log: slave.c,v $
 * Revision 1.14  1997/07/04 13:43:06  swen
 * Enhanced version of the previous bugfix.
 *
 * Revision 1.13  1997/07/04 13:17:02  swen
 * Add bugfix from Daryll Strauss <daryll@d2.com>: ping_server could destroy a
 * client handle, but only on a local copy. This could lead to destruction of
 * the client handle a second time -> boom.
 *
 * Revision 1.12  1997/07/04 10:10:32  swen
 * Added some bugfixes from Miquel van Smoorenburg. ypbind now falls back to
 * broadcast, if yp.conf does not exist or does not contain an entry for the
 * local domain.
 *
 * Revision 1.11  1997/05/27 11:39:45  swen
 * Added RCS id.
 *
 * Revision 1.10  1997/05/20 16:52:46  swen
 * More changes for autoconf. Documentation updates. Added no-ping option.
 *
 * Revision 1.9  1997/05/20 13:48:55  swen
 * Check if MAP_ANON is defined.
 *
 * Revision 1.8  1997/05/20 13:13:05  swen
 * More functional decomposition. Fixed bug, which prevented updating
 * the bindingfile. Fixed bug in rebingind code.
 *
 */
