예제 #1
0
파일: epmd_srv.c 프로젝트: RJ/otp
void run(EpmdVars *g)
{
  struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
  int listensock[MAX_LISTEN_SOCKETS];
  int num_sockets;
  int i;
  int opt;
  unsigned short sport = g->port;

  node_init(g);
  g->conn = conn_init(g);

#ifdef HAVE_SYSTEMD_DAEMON
  if (g->is_systemd)
    {
      int n;
      
      dbg_printf(g,2,"try to obtain sockets from systemd");

      n = sd_listen_fds(0);
      if (n < 0)
        {
          dbg_perror(g,"cannot obtain sockets from systemd");
          epmd_cleanup_exit(g,1);
        }
      else if (n == 0)
        {
          dbg_tty_printf(g,0,"systemd provides no sockets");
          epmd_cleanup_exit(g,1);
      }
      else if (n > MAX_LISTEN_SOCKETS)
      {
          dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", MAX_LISTEN_SOCKETS);
          epmd_cleanup_exit(g,1);
      } 
      num_sockets = n;
      for (i = 0; i < num_sockets; i++)
        {
          g->listenfd[i] = listensock[i] = SD_LISTEN_FDS_START + i;
        }
    }
  else
    {
#endif /* HAVE_SYSTEMD_DAEMON */

  dbg_printf(g,2,"try to initiate listening port %d", g->port);

  if (g->addresses != NULL && /* String contains non-separator characters if: */
      g->addresses[strspn(g->addresses," ,")] != '\000')
    {
      char *tmp;
      char *token;
      int loopback_ok = 0;

      if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
	{
	  dbg_perror(g,"cannot allocate memory");
	  epmd_cleanup_exit(g,1);
	}
      strcpy(tmp,g->addresses);

      for(token = strtok(tmp,", "), num_sockets = 0;
	  token != NULL;
	  token = strtok(NULL,", "), num_sockets++)
	{
	  struct EPMD_IN_ADDR addr;
#ifdef HAVE_INET_PTON
	  int ret;

	  if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
	    {
	      dbg_perror(g,"cannot convert IP address to network format");
	      epmd_cleanup_exit(g,1);
	    }
	  else if (ret == 0)
#elif !defined(EPMD6)
	  if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE)
#endif
	    {
	      dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token);
	      epmd_cleanup_exit(g,1);
	    }

	  if (IS_ADDR_LOOPBACK(addr))
	    loopback_ok = 1;

	  if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
	    {
	      dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
			     MAX_LISTEN_SOCKETS);
	      epmd_cleanup_exit(g,1);
	    }

	  SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport);
	}

      free(tmp);

      if (!loopback_ok)
	{
	  SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport);
	  num_sockets++;
	}
    }
  else
    {
      SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
      num_sockets = 1;
    }
#ifdef HAVE_SYSTEMD_DAEMON
    }
#endif /* HAVE_SYSTEMD_DAEMON */

#if !defined(__WIN32__)
  /* We ignore the SIGPIPE signal that is raised when we call write
     twice on a socket closed by the other end. */
  signal(SIGPIPE, SIG_IGN);
#endif

  /*
   * Initialize number of active file descriptors.
   * Stdin, stdout, and stderr are still open.
   */
  g->active_conn = 3 + num_sockets;
  g->max_conn -= num_sockets;

  FD_ZERO(&g->orig_read_mask);
  g->select_fd_top = 0;

#ifdef HAVE_SYSTEMD_DAEMON
  if (g->is_systemd)
      for (i = 0; i < num_sockets; i++)
          select_fd_set(g, listensock[i]);
  else
    {
#endif /* HAVE_SYSTEMD_DAEMON */
  for (i = 0; i < num_sockets; i++)
    {
      if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
	{
	  dbg_perror(g,"error opening stream socket");
	  epmd_cleanup_exit(g,1);
	}
      g->listenfd[i] = listensock[i];
  
      /*
       * Note that we must not enable the SO_REUSEADDR on Windows,
       * because addresses will be reused even if they are still in use.
       */
  
#if !defined(__WIN32__)
      opt = 1;
      if (setsockopt(listensock[i],SOL_SOCKET,SO_REUSEADDR,(char* ) &opt,
		     sizeof(opt)) <0)
	{
	  dbg_perror(g,"can't set sockopt");
	  epmd_cleanup_exit(g,1);
	}
#endif
  
      /* In rare cases select returns because there is someone
	 to accept but the request is withdrawn before the
	 accept function is called. We set the listen socket
	 to be non blocking to prevent us from being hanging
	 in accept() waiting for the next request. */
#if (defined(__WIN32__) || defined(NO_FCNTL))
      opt = 1;
      /* Gives warning in VxWorks */
      if (ioctl(listensock[i], FIONBIO, &opt) != 0)
#else
      opt = fcntl(listensock[i], F_GETFL, 0);
      if (fcntl(listensock[i], F_SETFL, opt | O_NONBLOCK) == -1)
#endif /* __WIN32__ || VXWORKS */
	dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
		   listensock[i]);

      if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i],
	  sizeof(iserv_addr[i])) < 0)
	{
	  if (errno == EADDRINUSE)
	    {
	      dbg_tty_printf(g,1,"there is already a epmd running at port %d",
			     g->port);
	      epmd_cleanup_exit(g,0);
	    }
	  else
	    {
	      dbg_perror(g,"failed to bind socket");
	      epmd_cleanup_exit(g,1);
	    }
	}

      if(listen(listensock[i], SOMAXCONN) < 0) {
          dbg_perror(g,"failed to listen on socket");
          epmd_cleanup_exit(g,1);
      }
      select_fd_set(g, listensock[i]);
    }
#ifdef HAVE_SYSTEMD_DAEMON
    }
    sd_notifyf(0, "READY=1\n"
                  "STATUS=Processing port mapping requests...\n"
                  "MAINPID=%lu", (unsigned long) getpid());
#endif /* HAVE_SYSTEMD_DAEMON */

  dbg_tty_printf(g,2,"entering the main select() loop");

 select_again:
  while(1)
    {
      fd_set read_mask = g->orig_read_mask;
      struct timeval timeout;
      int ret;

      /* If we are idle we time out now and then to enable the code
	 below to close connections that are old and probably
	 hanging. Make sure that select will return often enough. */

      timeout.tv_sec = (g->packet_timeout < IDLE_TIMEOUT) ? 1 : IDLE_TIMEOUT;
      timeout.tv_usec = 0;

      if ((ret = select(g->select_fd_top,
			&read_mask, (fd_set *)0,(fd_set *)0,&timeout)) < 0) {
	dbg_perror(g,"error in select ");
        switch (errno) {
          case EAGAIN:
          case EINTR:
            break;
          default:
            epmd_cleanup_exit(g,1);
        }
      }
      else {
	time_t now;
	if (ret == 0) {
	  FD_ZERO(&read_mask);
	}
	if (g->delay_accept) {		/* Test of busy server */
	  sleep(g->delay_accept);
	}

	for (i = 0; i < num_sockets; i++)
	  if (FD_ISSET(listensock[i],&read_mask)) {
	    if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) {
	      /*
	       * The accept() succeeded, and we have at least one file
	       * descriptor still free, which means that another accept()
	       * could succeed. Go do do another select(), in case there
	       * are more incoming connections waiting to be accepted.
	       */
	      goto select_again;
	    }
	  }
	  
	/* Check all open streams marked by select for data or a
	   close.  We also close all open sockets except ALIVE
	   with no activity for a long period */

	now = current_time(g);
	for (i = 0; i < g->max_conn; i++) {
	  if (g->conn[i].open == EPMD_TRUE) {
	    if (FD_ISSET(g->conn[i].fd,&read_mask))
	      do_read(g,&g->conn[i]);
	    else if ((g->conn[i].keep == EPMD_FALSE) &&
		     ((g->conn[i].mod_time + g->packet_timeout) < now)) {
	      dbg_tty_printf(g,1,"closing because timed out on receive");
	      epmd_conn_close(g,&g->conn[i]);
	    }
	  }
	}
      }
    }
}
예제 #2
0
파일: epmd_srv.c 프로젝트: RJ/otp
static void do_read(EpmdVars *g,Connection *s)
{
  int val, pack_size;

  if (s->open == EPMD_FALSE)
    {
      dbg_printf(g,0,"read on unknown socket");
      return;
    }

  /* Check if we already got the whole packet but we keep the
     connection alive to find out when a node is terminated. We then
     want to check for a close */

  if (s->keep == EPMD_TRUE)
    {
      val = read(s->fd, s->buf, INBUF_SIZE);

      if (val == 0)
	{
	  node_unreg_sock(g,s->fd);
	  epmd_conn_close(g,s);
	}
      else if (val < 0)
	{
	    dbg_tty_printf(g,1,"error on ALIVE socket %d (%d; errno=0x%x)",
			   s->fd, val, errno);
	  node_unreg_sock(g,s->fd);
	  epmd_conn_close(g,s);
	}
      else
	{
	  dbg_tty_printf(g,1,"got more than expected on ALIVE socket %d (%d)",
		 s->fd,val);
	  dbg_print_buf(g,s->buf,val);

	  node_unreg_sock(g,s->fd);
	  epmd_conn_close(g,s);
	}
      return;
    }

  /* If unknown size we request the whole buffer - what we got - 1
     We subtract 1 because we will add a "\0" in "do_request()".
     This is not needed for R3A or higher versions of Erlang,
     because the '\0' is included in the request,
     but is kept for backwards compatibility to allow R2D to use
     this epmd. */

  pack_size = s->want ? s->want : INBUF_SIZE - 1;
  val = read(s->fd, s->buf + s->got, pack_size - s->got);

  if (val == 0)
    {
      /* A close when we haven't got all data */
      dbg_printf(g,0,"got partial packet only on file descriptor %d (%d)",
		 s->fd,s->got);
      epmd_conn_close(g,s);
      return;
    }

  if (val < 0)
    {
      dbg_perror(g,"error in read");
      epmd_conn_close(g,s);
      return;
    }

  dbg_print_buf(g,s->buf,val);

  s->got += val;

  if ((s->want == 0) && (s->got >= 2))
    {
      /* The two byte header that specify the length of the packet
	 doesn't count the header as part of the packet so we add 2
	 to "s->want" to make us talk about all bytes we get. */

      s->want = get_int16(s->buf) + 2;

      if ((s->want < 3) || (s->want >= INBUF_SIZE))
	{
	  dbg_printf(g,0,"invalid packet size (%d)",s->want - 2);
	  epmd_conn_close(g,s);
	  return;
	}

      if (s->got > s->want)
	{
	  dbg_printf(g,0,"got %d bytes in packet, expected %d",
		     s->got - 2, s->want - 2);
	  epmd_conn_close(g,s);
	  return;
	}
    }
  
  s->mod_time = current_time(g); /* Note activity */
  
  if (s->want == s->got)
    {
      /* Do action and close up */
      /* Skip header bytes */

      do_request(g, s->fd, s, s->buf + 2, s->got - 2);

      if (!s->keep)
	epmd_conn_close(g,s);		/* Normal close */
    }
}
예제 #3
0
파일: epmd_srv.c 프로젝트: LittleForker/otp
void run(EpmdVars *g)
{
  int listensock;
  int i;
  int opt;
  struct EPMD_SOCKADDR_IN iserv_addr;

  node_init(g);
  g->conn = conn_init(g);

  dbg_printf(g,2,"try to initiate listening port %d", g->port);
  
  if ((listensock = socket(FAMILY,SOCK_STREAM,0)) < 0) {
    dbg_perror(g,"error opening stream socket");
    epmd_cleanup_exit(g,1);
  }
  g->listenfd = listensock;

  /*
   * Initialize number of active file descriptors.
   * Stdin, stdout, and stderr are still open.
   * One for the listen socket.
   */
  g->active_conn = 3+1;
  
  /*
   * Note that we must not enable the SO_REUSEADDR on Windows,
   * because addresses will be reused even if they are still in use.
   */
  
#if !defined(__WIN32__)
  /* We ignore the SIGPIPE signal that is raised when we call write
     twice on a socket closed by the other end. */
  signal(SIGPIPE, SIG_IGN);

  opt = 1;			/* Set this option */
  if (setsockopt(listensock,SOL_SOCKET,SO_REUSEADDR,(char* ) &opt,
		 sizeof(opt)) <0) {
    dbg_perror(g,"can't set sockopt");
    epmd_cleanup_exit(g,1);
  }
#endif
  
  /* In rare cases select returns because there is someone
     to accept but the request is withdrawn before the
     accept function is called. We set the listen socket
     to be non blocking to prevent us from being hanging
     in accept() waiting for the next request. */
#if (defined(__WIN32__) || defined(NO_FCNTL))
  opt = 1;
  if (ioctl(listensock, FIONBIO, &opt) != 0) /* Gives warning in VxWorks */
#else
  opt = fcntl(listensock, F_GETFL, 0);
  if (fcntl(listensock, F_SETFL, opt | O_NONBLOCK) == -1)
#endif /* __WIN32__ || VXWORKS */
    dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
	       listensock);

  { /* store port number in unsigned short */
    unsigned short sport = g->port;
    SET_ADDR_ANY(iserv_addr, FAMILY, sport);
  }
  
  if(bind(listensock,(struct sockaddr*) &iserv_addr, sizeof(iserv_addr)) < 0 )
    {
      if (errno == EADDRINUSE)
	{
	  dbg_tty_printf(g,1,"there is already a epmd running at port %d",
			 g->port);
	  epmd_cleanup_exit(g,0);
	}
      else
	{
	  dbg_perror(g,"failed to bind socket");
	  epmd_cleanup_exit(g,1);
	}
    }

  dbg_printf(g,2,"starting");

  if(listen(listensock, SOMAXCONN) < 0) {
      dbg_perror(g,"failed to listen on socket");
      epmd_cleanup_exit(g,1);
  }

  FD_ZERO(&g->orig_read_mask);
  FD_SET(listensock,&g->orig_read_mask);

  dbg_tty_printf(g,2,"entering the main select() loop");

 select_again:
  while(1)
    {	
      fd_set read_mask = g->orig_read_mask;
      struct timeval timeout;
      int ret;

      /* If we are idle we time out now and then to enable the code
	 below to close connections that are old and probably
	 hanging. Make sure that select will return often enough. */

      timeout.tv_sec = (g->packet_timeout < IDLE_TIMEOUT) ? 1 : IDLE_TIMEOUT;
      timeout.tv_usec = 0;

      if ((ret = select(g->max_conn,&read_mask,(fd_set *)0,(fd_set *)0,&timeout)) < 0) {
	dbg_perror(g,"error in select ");
        switch (errno) {
          case EAGAIN:
          case EINTR:
            break;
          default:
            epmd_cleanup_exit(g,1);
        }
      }
      else {
	time_t now;
	if (ret == 0) {
	  FD_ZERO(&read_mask);
	}
	if (g->delay_accept) {		/* Test of busy server */
	  sleep(g->delay_accept);
	}

	if (FD_ISSET(listensock,&read_mask)) {
	  if (do_accept(g, listensock) && g->active_conn < g->max_conn) {
	    /*
	     * The accept() succeeded, and we have at least one file
	     * descriptor still free, which means that another accept()
	     * could succeed. Go do do another select(), in case there
	     * are more incoming connections waiting to be accepted.
	     */
	    goto select_again;
	  }
	}
	  
	/* Check all open streams marked by select for data or a
	   close.  We also close all open sockets except ALIVE
	   with no activity for a long period */

	now = current_time(g);
	for (i = 0; i < g->max_conn; i++) {
	  if (g->conn[i].open == EPMD_TRUE) {
	    if (FD_ISSET(g->conn[i].fd,&read_mask))
	      do_read(g,&g->conn[i]);
	    else if ((g->conn[i].keep == EPMD_FALSE) &&
		     ((g->conn[i].mod_time + g->packet_timeout) < now)) {
	      dbg_tty_printf(g,1,"closing because timed out on receive");
	      epmd_conn_close(g,&g->conn[i]);
	    }
	  }
	}
      }
    }
}