#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include "tcp_listen.h"
#include <netdb.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

static void Setsockopt(int fd, int level, int optname, const void * optval, socklen_t optlen) {
  if (setsockopt(fd,level,optname,optval,optlen) < 0) {
    syslog(LOG_WARNING,"%s: %s","setsockopt failed",strerror(errno));
  }
}

static void Listen(int fd, int backlog) {
  char * ptr;

  if ((ptr = getenv("LISTENQ")) != NULL) {
    backlog = atoi(ptr);
  }

  if (listen(fd,backlog) < 0) {
    syslog(LOG_ERR,"%s: %s","listen failed",strerror(errno));
    exit(EXIT_FAILURE);
  }
}

static int tcp_listen(const char * host, const char * serv, socklen_t * addrlenp) {
  int listenfd, n;
  const int on = 1;
  struct addrinfo hints, * res, * ressave;
  struct linger sl = { 1, 5 };

  memset(&hints,0,sizeof(struct addrinfo));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  if ((n = getaddrinfo(host,serv,&hints,&res)) != 0) {
    syslog(LOG_ERR,"%s: %s, %s: %s","getaddrinfo failed",host,serv,strerror(errno));
    perror("getaddrinfo");
    exit(EXIT_FAILURE);
  }
  ressave = res;

  do {
    listenfd = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    if (listenfd < 0)
      continue;
    Setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    Setsockopt(listenfd,SOL_SOCKET,SO_LINGER,&sl,sizeof(sl));
    if (bind(listenfd,res->ai_addr,res->ai_addrlen) == 0)
      break;
    close(listenfd);
  } while ((res = res->ai_next) != NULL);
  if (res == NULL) {
    syslog(LOG_ERR,"%s: %s, %s: %s","failed to bind socket",host,serv,strerror(errno));
    perror("tcp_listen");
    exit(EXIT_FAILURE);
  }
  Listen(listenfd,LISTENQ);
  if (addrlenp) 
    *addrlenp = res->ai_addrlen;

  freeaddrinfo(ressave);
  return listenfd;
}

int Tcp_listen(const char * host, const char * serv, socklen_t * addrlenp) {
  return tcp_listen(host,serv,addrlenp);
}
