#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define	SERVER	"sock_s"
#define	NALLOC	10

int	listen_fd = -1;

void terminate(int rc)
{
  if(listen_fd >= 0){
    shutdown(listen_fd, 2);
    close(listen_fd);
    unlink(SERVER);
  }
  exit(rc);
}

int main()
{
  int	i, fd, rc, len, maxfd;
  int	fd_size, *fd_list;
  fd_set	rset, allset;
  struct sockaddr_un	unix_addr;
  char	buf[256];

  signal(SIGINT, terminate);
  signal(SIGTERM, terminate);

  /* ソケットを作る	*/
  listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
  if(listen_fd < 0){
    printf("server:socket error.\n");
    terminate(rc);
  }
  printf("server:socket.\n");

  /* ソケットに名前を付ける(SERVER)	*/
  unix_addr.sun_family = AF_UNIX;
  strcpy(unix_addr.sun_path, SERVER);
  len = sizeof(unix_addr.sun_family)+strlen(unix_addr.sun_path);
  rc = bind(listen_fd, (struct sockaddr *)&unix_addr, len);
  if(rc < 0){
    printf("server:bind error.\n");
    terminate(rc);
  }
  printf("server:bind.\n");

  /* SERVERはconnect可能だよ	*/
  rc = listen(listen_fd, 5);
  if(rc < 0){
    printf("server:listen error.\n");
    terminate(rc);
  }
  printf("server:listen.\n");

  /* 待ちリストの初期化	*/
  FD_ZERO(&allset);
  FD_SET(listen_fd, &allset);
  maxfd = listen_fd;
  fd_size = NALLOC;
  fd_list = malloc(sizeof(int)*fd_size);
  for(i = 0; i < fd_size; i++){
    fd_list[i] = -1;
  }

  /* 無限ループ	*/
  for(;;){
    /* allsetにセットされているfdから何か言ってくるのを待つ	*/
    memcpy(&rset, &allset, sizeof(rset));
    rc = select(maxfd+1, &rset, NULL, NULL, NULL);
    if(rc < 0){
      printf("server:select error.\n");
      terminate(rc);
    }
    printf("server:select.\n");

    /* 来たのがlisten_fdだったら	*/
    if(FD_ISSET(listen_fd, &rset)){
      /* 新たなconnectなのでaccept	*/
      len = sizeof(unix_addr);
      fd = accept(listen_fd, (struct sockaddr *)&unix_addr, &len);
      if(fd < 0){
	printf("server:accept error.\n");
	terminate(fd);
      }
      printf("server:accept (%d).\n", fd);
      /* そのfdをsellectで待つリストに追加	*/
      for(i = 0; i < fd_size; i++){
	if(fd_list[i] == -1){
	  fd_list[i] = fd;
	  break;
	}
      }
      if(i >= fd_size){
	fd_list = realloc(fd_list, sizeof(int)*(fd_size+NALLOC));
	for(i = fd_size; i < fd_size+NALLOC; i++){
	  fd_list[i] = -1;
	}
	fd_list[fd_size] = fd;
	fd_size += NALLOC;
      }
      FD_SET(fd, &allset);
      if(fd > maxfd){
	maxfd = fd;
      }
    }

    /* 来たのがlisten_fdでなかったら	*/
    else {
      for(i = 0; i < fd_size; i++){
	if((fd = fd_list[i]) != -1){
	  if(FD_ISSET(fd, &rset)){
	    /* acceptしたソケットから受信	*/
	    rc = recv(fd, buf, 256, 0);
	    /* 受信サイズが負だったら	*/
	    if(rc < 0){
	      /* 異常処理	*/
	      printf("server:recv (%d) error.\n", fd);
	      terminate(rc);
	    }
	    /* 受信サイズが0だったら	*/
	    else if(rc == 0){
	      /* そのfdはクローズしたのでリストから削除	*/
	      printf("server:closed (%d).\n", fd);
	      fd_list[i] = -1;
	      FD_CLR(fd, &allset);
	      close(fd);
	    }
	    /* 受信サイズが正だったら	*/
	    else {
	      /* 受信したメッセージを表示	*/
	      printf("server:recv (%d) %s.\n", fd, buf);
	    }
	  }
	}
      }
    }
  }

  terminate(0);

  return 0;
}
