/***************************************
  $Revision: 1.9 $

  Example code: A thread.

  Status: NOT REVUED, NOT TESTED

 Authors:       Chris Ottrey
		Joao Damas

  +html+ <DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL>
  +html+ </DL>
 
  ******************/ /******************
  Modification History:
        ottrey (02/03/1999) Created.
        ottrey (08/03/1999) Modified.
        ottrey (17/06/1999) Stripped down.
        joao   (22/06/1999) Redid thread startup
  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/
#include <pthread.h>       /* Posix thread library */
#include <stdio.h>
#include <strings.h>

#include "thread.h"
#include "socket.h"
#include "protocol_whois.h"
#include "protocol_config.h"
#include "constants.h"

/*+ String sizes +*/
#define STR_S   63
#define STR_M   255
#define STR_L   1023
#define STR_XL  4095
#define STR_XXL 16383

/*+ Mutex lock.  Used for synchronizing changes. +*/
pthread_mutex_t   Whois_thread_count_lock;
pthread_mutex_t   Config_thread_count_lock;
pthread_mutex_t   Mirror_thread_count_lock;

/*+ The number of threads. +*/
int       Whois_thread_count;
int       Config_thread_count;
int       Mirror_thread_count;

typedef struct th_args {
	void * function;
	int sock;
} th_args;

static void log_print(const char *arg) {
  FILE *logf;

  if (CO_get_thread_logging() == 1) {
    if (strcmp(CO_get_thread_logfile(), "stdout") == 0) {
      printf(arg);
    }
    else {
      logf = fopen(CO_get_thread_logfile(), "a");
      fprintf(logf, arg);
      fclose(logf);
    }
  }

} /* log_print() */
 
/* TH_acquire_read_lock() */
/*++++++++++++++++++++++++++++++++++++++

  Aquire a readers lock.

  rw_lock_t *prw_lock Readers writers lock.

  Reference: "Multithreaded Programming Techniques - Prasad p.192"
  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_acquire_read_lock(rw_lock_t *prw_lock) { 
  pthread_mutex_lock(&prw_lock->rw_mutex);

  while (prw_lock->rw_count < 0) {
    pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
  }

  ++prw_lock->rw_count;
  pthread_mutex_unlock(&prw_lock->rw_mutex);

} /* TH_acquire_read_lock() */

/* TH_release_read_lock() */
/*++++++++++++++++++++++++++++++++++++++

  Release a readers lock.

  rw_lock_t *prw_lock Readers writers lock.

  Reference: "Multithreaded Programming Techniques - Prasad p.192"
  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_release_read_lock(rw_lock_t *prw_lock) { 
  pthread_mutex_lock(&prw_lock->rw_mutex);

  --prw_lock->rw_count;

  if (!prw_lock->rw_count) {
    pthread_cond_signal(&prw_lock->rw_cond);
  }

  pthread_mutex_unlock(&prw_lock->rw_mutex);

} /* TH_release_read_lock() */

/* TH_acquire_write_lock() */
/*++++++++++++++++++++++++++++++++++++++

  Aquire a writers lock.

  rw_lock_t *prw_lock Readers writers lock.

  Reference: "Multithreaded Programming Techniques - Prasad p.192"
  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_acquire_write_lock(rw_lock_t *prw_lock) { 
  pthread_mutex_lock(&prw_lock->rw_mutex);

  while (prw_lock->rw_count != 0) {
    pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
  }

  prw_lock->rw_count = -1;
  pthread_mutex_unlock(&prw_lock->rw_mutex);

} /* TH_acquire_write_lock() */

/* TH_release_write_lock() */
/*++++++++++++++++++++++++++++++++++++++

  Release a writers lock.

  rw_lock_t *prw_lock Readers writers lock.

  Reference: "Multithreaded Programming Techniques - Prasad p.192"
  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_release_write_lock(rw_lock_t *prw_lock) { 
  pthread_mutex_lock(&prw_lock->rw_mutex);
  prw_lock->rw_count = 0;
  pthread_mutex_unlock(&prw_lock->rw_mutex);
  pthread_cond_broadcast(&prw_lock->rw_cond);

} /* TH_release_write_lock() */

/* TH_init_read_write_lock() */
/*++++++++++++++++++++++++++++++++++++++

  Initialize a readers/writers lock.

  rw_lock_t *prw_lock Readers writers lock.

  Side effect: the lock is set to open(?)

  Reference: "Multithreaded Programming Techniques - Prasad p.192"
  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_init_read_write_lock(rw_lock_t *prw_lock) { 
  pthread_mutex_init(&prw_lock->rw_mutex, NULL);
  pthread_cond_init(&prw_lock->rw_cond, NULL);
  prw_lock->rw_count = 0;

} /* TH_init_read_write_lock() */

int TH_get_id(void) {

  return (int)pthread_self();

} /* TH_get_id() */

/* TH_to_string() */
char *TH_to_string(void) {
  char *thread_info;
  char tmp[STR_L];
  char thread_info_buffer[STR_XL];

  strcpy(thread_info_buffer, "Thread = { ");

  sprintf(tmp, "[pthread_self] = \"%d\" ", pthread_self());
  strcat(thread_info_buffer, tmp);
  
  /*
  thread_name = (char *)pthread_getspecific(Name);

  if (thread_name == NULL ) {
    sprintf(tmp, "[Name] = \"%s\" ", "didn't work!");
  }
  else {
    sprintf(tmp, "[Name] = \"%s\" ", thread_name);
  }
  strcat(thread_info_buffer, tmp);
  */
  
  strcat(thread_info_buffer, "}");
  
  thread_info = (char *)calloc(1, strlen(thread_info_buffer)+1);
  strcpy(thread_info, thread_info_buffer);

  return thread_info;
} /* TH_to_string() */

/* TH_do_whois() */
/*++++++++++++++++++++++++++++++++++++++

  Handle whois connections.

  void *arg The socket to connect to. (It has to be passed in this way for this thread routine.)

  More:
  +html+ <PRE>
  Author:
        joao
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_do_whois(void *arg) { 
  int sock = (int)arg;
  char print_buf[STR_M];

  sprintf(print_buf, "Whois: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");

  /* Use a mutex to update the global whois thread counter. */
  pthread_mutex_lock(&Whois_thread_count_lock);
  Whois_thread_count++;
  sprintf(print_buf, "Whois_thread_count++=%d\n", Whois_thread_count); log_print(print_buf); strcpy(print_buf, "");
  pthread_mutex_unlock(&Whois_thread_count_lock);

  PW_interact(sock);

  /* Use a mutex to update the global whois thread counter. */
  pthread_mutex_lock(&Whois_thread_count_lock);
  Whois_thread_count--;
  sprintf(print_buf, "Whois_thread_count--=%d\n", Whois_thread_count); log_print(print_buf); strcpy(print_buf, "");
  pthread_mutex_unlock(&Whois_thread_count_lock);

  pthread_exit((void *)0);

} /* TH_do_whois() */

/* TH_do_config() */
/*++++++++++++++++++++++++++++++++++++++

  Handle config connections.

  void *arg The socket to connect to. (It has to be passed in this way for this
thread routine.)

  More:
  +html+ <PRE>
  Author:
        joao
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_do_config(void *arg) {
  int sock = (int)arg;
  char print_buf[STR_M];

  sprintf(print_buf, "Config: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");

/*
  printf("Hi there, there is nothing to configure yet\nBye..... :-)\n");
  fflush(NULL);

  SK_close(sock);
*/
  PC_interact(sock);

  pthread_exit((void *)0);

} /* TH_do_config() */

/* main_thread() */
/*++++++++++++++++++++++++++++++++++++++

  Waits for an incoming connection on the and spawns a new thread to handle it.

  void *arg Pointer to a struct containing the socket to talk to the client and
            the function to call depending on the incoming connection.

  More:
  +html+ <PRE>
  Author:
        ottrey
	joao
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
static void  *main_thread(void *arg) {
  th_args *args = (th_args *)arg;
  pthread_t tid;
  pthread_attr_t attr;
  int connected_socket;

  while(1) {

    connected_socket = SK_accept_connection(args->sock);

    /* Start a new thread. */

    pthread_attr_init(&attr);    /* initialize attr with default attributes */
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, (void *(*)(void *))(args->function), (void *)connected_socket); 
  }

} /* main_thread() */

/* TH_run() */
/*++++++++++++++++++++++++++++++++++++++

  This is the routine that creates the main threads. 

  int     sock        The socket to connect to.
  void *  do_function The function to call for each type of service

  More:
  +html+ <PRE>
  Author:
        ottrey
	joao
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
void TH_run(int sock, void *do_function) {
  th_args *args;
  pthread_t tid;
  pthread_attr_t attr;
  char print_buf[STR_M];

  int connected_socket;

  args = (th_args *)calloc(1,sizeof(th_args));
  args->function=do_function;
  args->sock=sock;

/*  pthread_mutex_init(&Whois_thread_count_lock,NULL); */

  if ( CO_get_max_threads() == 0 ) {
    sprintf(print_buf, "Running with no threads\n"); log_print(print_buf); strcpy(print_buf, "");
    connected_socket = SK_accept_connection(sock);
    PW_interact(connected_socket);
  }
  else {
    /* Start a new thread. */
    pthread_attr_init(&attr);     /* initialize attr with default attributes */
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, main_thread, (void *)args);
  }

} /* TH_run() */

void TH_run2(void *function) {
  pthread_t tid;
  pthread_attr_t attr;

  /* Start a new thread. */
  pthread_attr_init(&attr);     /* initialize attr with default attributes */
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  pthread_create(&tid, &attr, function, (void *)0);

} /* TH_run2() */
