
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<netinet/in_systm.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<errno.h>

#include"zither.h"
#include<almemsys/almemsys.h>
#include"jpreturn.h"

//#ifdef POSTGRES_IN
//  #include<libpq-fe.h>
//  PGconn * conn = 0;
//  PGresult * res;
//#endif

//#ifdef MYSQL_IN
//  #include<mysql.h>
//  MYSQL mysql_buf;
//  MYSQL * my_conn = 0;
//  MYSQL_RES * my_result;
//#endif


global_data_t gd;

// pthread_mutex_t sig_mutex;

// V6 対応

int main(int argc, char * argv[])
{
pthread_t t1;
#ifdef SIGNATURE_MODE
pthread_t t2;
#endif

pthread_t t3;

long int fdcount;
int tret;
// int error;
int i;
int pt;
int retval;
char bu[256];

// int smax;

char * buf = bu;

retval = 0;
pt = 0;
errno = 0;
tret = 0;

pthread_mutex_init(&sig_mutex, NULL);
pthread_mutex_init(&stat_mutex, NULL);
pthread_mutex_init(&name_mutex, NULL);

signal(SIGINT, (void *)signal_exit);
signal(SIGKILL, (void *)signal_exit);
signal(SIGQUIT, (void *)signal_exit);
signal(SIGHUP, (void *)signal_exit);
signal(SIGPIPE, (void *)signal_errout);
// signal(SIGTERM, signal_exit);  // BSDの為

gd.td.midcount = B_MID;   // mid max
gd.td.midint = MID_INT;   // mid auto dump interval

malloc_set();
set_data();
optin(argc, argv);
conf_set(gd.fp.conf_path);
setup_check();

#ifdef PRINT_CONF
print_conf();
#endif


printf("main(): neg=%s, lock=%s, conf=%s\n", gd.fp.neg_conf, gd.fp.lock_path, gd.fp.file_conf);
lockfile_remove(gd.fp.neg_conf, gd.fp.lock_path, gd.fp.file_conf);
malloc_set2();

if(gd.exid.uid <= 0){
   fprintf(stderr,"main(): Not exec user id. %d\n", gd.exid.uid);
   exit(-1);
   }
if(setuid(gd.exid.uid)){
   fprintf(stderr,"main(): Not setuid err %d\n", gd.exid.uid);
   exit(-1);
   }

#ifdef ERR_OUT
pthread_mutex_init(&err_mutex, NULL);
gd.fp.err_fd = fd_outopen(gd.fp.errlog, gd.fp.err_fp);
#endif
#ifdef LOG_OUT
pthread_mutex_init(&con_mutex, NULL);
gd.fp.con_fd = fd_outopen(gd.fp.connectlog, gd.fp.con_fp);
#endif

#ifdef POSTGRES_IN
pthread_mutex_init(&pg_mutex, NULL);
gd.pg.stat = psqlconnect();
#endif

#ifdef MYSQL_IN
pthread_mutex_init(&mysql_mutex, NULL);
gd.mysql.stat = mysqlconnect(gd.mysql.mysql_server, gd.mysql.mysql_username, gd.mysql.mysql_password, gd.mysql.mysql_db);
#ifdef MYSQL_DEBUG
printf("MYSQL_DEBUG: zither.c: MYSQL CONNECTION STATUS <%d>\n", gd.mysql.stat);
#endif
#endif


// IPにポート番号をバインド
memset(&gd.ms.hints, 0, sizeof(gd.ms.hints));

gd.ms.hints.ai_socktype     = SOCK_STREAM;
// gd.ms.hints.ai_socktype  = SOCK_DGRAM;   /* Datagram socket */
gd.ms.hints.ai_flags        = AI_PASSIVE;   /* For wildcard IP address */
gd.ms.hints.ai_family       = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
gd.ms.hints.ai_protocol     = 0;            /* Any protocol */
gd.ms.hints.ai_canonname    = NULL;
gd.ms.hints.ai_addr         = NULL;
gd.ms.hints.ai_next         = NULL;

if(0 == self_memcmp(gd.ms.ip_addr, "INADDR_ANY")){
   gd.ms.smax = socket_bind46(&gd.ms.hints, NULL, gd.ms.chport, gd.ms.ss, SOCKMAX);
   }
else{
   gd.ms.smax = socket_bind46(&gd.ms.hints, gd.ms.ip_addr, gd.ms.chport, gd.ms.ss, SOCKMAX);
   }
if(0 >= gd.ms.smax){
   fprintf(stderr,"zither(): IP %s をバインド出来ない為、致し方なく死亡します \n", gd.ms.ip_addr);
   exit(1);
   }


// bind でえたFDをselect用にFD_SETしていく
FD_ZERO(&gd.ms.rfd0);
for(i = 0; i < gd.ms.smax; i++){
   printf("main(): BIND IP FD_SET: i = %d ls = %d\n", i, gd.ms.ss[i]);
   FD_SET(gd.ms.ss[i], &gd.ms.rfd0);
   }




// メール完結処理用のバッファーを復元
safe_pthread_mutex_lock(&name_mutex);
miname_install();
pthread_mutex_unlock(&name_mutex);

// client stop checker thread 
if((tret = pthread_create( &t1, NULL, (void *)clockfile_check, NULL)) != 0){
   fprintf(stderr,"zither.c: main(0): t1 pthread_create %d\n", tret);
   }

#ifdef SIGNATURE_MODE
// mail sign check 
if((tret = pthread_create( &t2, NULL, (void *)mailsigfile_check, NULL)) != 0){
   fprintf(stderr,"zither.c: main(1): t2 pthread_create %d\n", tret);
   }
#endif

// MIDネームリスト自動生成スレッドの生成
if((tret = pthread_create( &t3, NULL, (void *)th_miname_dump, NULL)) != 0){
   fprintf(stderr,"zither.c pthread_create(1): %d %d\n",tret, errno);
   }


// こねくしょんの受付無限ループ
for(;;){

   // fdcount ユニーク番号を探す
   safe_pthread_mutex_lock(&stat_mutex);
   for(fdcount = 0; fdcount < gd.ms.max_count; fdcount++){
      if(gd.ms.fdstat[fdcount] != 1){
         gd.ms.fdstat[fdcount] = 1;
         break;
         }
      } 
   #ifdef DEBUG
   fprintf(stderr,"DEBUG: main(): new connection fdno = %d\n",fdcount); 
   #endif
   pthread_mutex_unlock(&stat_mutex);

   // fdcount ユニーク番号が上限に達していないのでaceeptでまってみる（正常）
   if(fdcount < gd.ms.max_count){

      gd.ms.rfd = gd.ms.rfd0;
      retval = select(SOCKMAX + 1, &gd.ms.rfd, NULL, NULL, NULL);
      // selectがヘンな場合
      if(retval < 0){
         perror("select");
         break;
         }
      // selectがFDの変化を察知した場合
      else{
         // どのIPへのソケットが調べる
         for(i = 0; i < gd.ms.smax; i++){
            if(FD_ISSET(gd.ms.ss[i], &gd.ms.rfd)){
               gd.ms.fromlen = sizeof(gd.ms.from);
               gd.ms.sk[fdcount] = accept(gd.ms.ss[i], (struct sockaddr *)&gd.ms.from, (socklen_t *)&gd.ms.fromlen);
               // acceptで得たFDが0より小さい（異常）
               if(gd.ms.sk[fdcount] < 0){
                  printf("ip addr %s sock id = %d  proto Version = %d \n",
                    gd.ms.inip_addr[fdcount], gd.ms.sk[fdcount], pt);
                  #ifdef THREAD_BUG
                  fprintf(stderr,"THREAD_BUG: main() accept()に何らかのエラー. 番号 %ld\n", fdcount);
                  #endif
                  }
               // 正常にFDを得た場合（正常）
               else{
                  // IPアドレスの取得
                  if(gd.ms.from.ss_family == AF_INET){
                     gd.ms.sin = (struct sockaddr_in *)&gd.ms.from;
                     pt = 4;
                     gd.ms.inip_addr[fdcount] = safe_memcpy(
                       gd.ms.inip_addr[fdcount], (char *)inet_ntop(AF_INET, &(gd.ms.sin->sin_addr), buf, 256), BUF_LEN);
                     }
                  else if(gd.ms.from.ss_family == AF_INET6){
                     gd.ms.sin6 = (struct sockaddr_in6 *)&gd.ms.from;
                     pt = 6;
                     gd.ms.inip_addr[fdcount] = safe_memcpy(
                       gd.ms.inip_addr[fdcount], (char *)inet_ntop(AF_INET6, &(gd.ms.sin6->sin6_addr), buf, 256), BUF_LEN);
                     }
                  else{
                     pt = 0;
                     printf("not accepted connection fer v6 or v4\n");
                     }

                  // connect_open スレッドの生成
      //          gd.ms.fdno = fdcount;
                  printf("NEW CONNECTION: ip addr = %s sock id = %d  IP Version = %d \n",
                    gd.ms.inip_addr[fdcount], gd.ms.sk[fdcount], pt);
      //          if((tret = pthread_create(&gd.pt[fdcount], NULL, (void *)connect_open, (long int *)gd.ms.fdno)) != 0){
                  if((tret = pthread_create(&gd.pt[fdcount], NULL, (void *)connect_open, (long int *)fdcount)) != 0){
                     fprintf(stderr,"zither.c: main(1): pthread_create %d\n", tret);
                     }
                  #ifdef THREAD_BUG
                  fprintf(stderr,"THREAD_BUG: main(): 新しいスレッドが生成されました. 番号 %ld\n", fdcount);
                  #endif

                  }
               }
            }
         }
      }

   // ユニーク番号が上限に達している、10秒まってLoopしてみる（異常）
   else {
      fprintf(stderr,"main(): スレッド生成数の上限に達しています、10秒ほど待ってみます.%ld\n", fdcount);
      sleep(10); 
      }
   }


fprintf(stderr,"まあ通常ここには到達しないね\n");


pthread_join( t1, NULL);
#ifdef SIGNATURE_MODE
pthread_join( t2, NULL);
#endif
pthread_join( t3, NULL);

malloc_free();

/* --- It doesn't reach. ---*/
close(gd.ms.ls);
return 0;
}





