/*****************************************************************************
  *                             Loader
  *  
  */
  
#include <stdio.h>
#include <curl/curl.h>
#include <sys/timeb.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <signal.h>

   struct thread_data{
	int threads;
   int  thread_id;
	//int exec; //total number of executions 
	int max_req_client;
	int random; //1=yes 0=no whether requests are the max or randomdouble
	int ssl; //1=yes 0=no
	int uselogin; //1=yes 0=no
	char domain[256];
	char login[256];
	char password[256];
	
	};
	
  struct MemoryStruct {
    char *memory;
    size_t size;
  };

 void *myrealloc(void *ptr, size_t size)
 {
   /* There might be a realloc() out there that doesn't like reallocing
      NULL pointers, so we take care of it here */
   if(ptr)
     return realloc(ptr, size);
   else
     return malloc(size);
 }
 
 size_t
 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
 {
   size_t realsize = size * nmemb;
   struct MemoryStruct *mem = (struct MemoryStruct *)data;
 
   mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
   if (mem->memory) {
     memcpy(&(mem->memory[mem->size]), ptr, realsize);
     mem->size += realsize;
     mem->memory[mem->size] = 0;
   }
   return realsize;
 }


sem_t sem_reqpass, sem_reqfail, sem_totallogins, sem_timelogins, sem_timereqs;

long endedvar, startthreads,reqpass, reqfail, runtime, totallogins, starttime, timelogins, timereqs;


void printerror(){

printf("Signal 6 was received\n");
}
  
long start(){
	return startthreads;
}//start

long ended(){
	return endedvar;	
}

void setended(){
	endedvar =1;
}

void addtotalreqpass(long input){
	sem_wait(&sem_reqpass);
    reqpass += input;
    sem_post(&sem_reqpass);
}

void addtotalreqfail(long input){
	sem_wait(&sem_reqfail);
    reqfail += input;
    sem_post(&sem_reqfail);
}

long addtotaltime(long input){
	//minus currenttime from starttime
	return input - starttime;
}

void addtotallogins(long input){
	sem_wait(&sem_totallogins);
    totallogins += input;
    sem_post(&sem_totallogins);	
}

 void addtimelogins(long input){
	sem_wait(&sem_timelogins);
    timelogins += input;
    sem_post(&sem_timelogins);
 }
 
 void addtimereqs(long input){
	sem_wait(&sem_timereqs);
    timereqs += input;
    sem_post(&sem_timereqs);
 }

 void *child_create(void *threadarg){
	 
	 //structure to pass data to the threads
	struct thread_data *my_data;
	my_data = (struct thread_data *) threadarg;   
	int taskid = my_data->thread_id;
	int threads = my_data->threads;
	int max_req_client = my_data->max_req_client;
	 	 
	//Create thread
	 printf("Creating thread %d \n", taskid);	 
   
	while(start()){ //printf("startwaiting");
		sleep(1);
		if(threads == taskid) startthreads=0;
	}//while not start
	printf("s");

	
	/*
	int taskid=0;
	int max_req_client =30;
	*/
	long sec, msec;
	struct MemoryStruct chunk;
    	chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
    	chunk.size = 0;    /* no data at this point */
	struct timeb t1,t2; //timing structure
	long counter =0;
	char *data="username=jeremy1&password=test";
   
while(1){
	
  if(ended()){
	  return;
	  //pthread_exit((void *) 0);
  }//stopping thread
	
     //new curl handle to restart the connection
	CURL *curl;
    CURLcode res;

	 curl = curl_easy_init();
    if(curl) { //continue if curl can be initialized
		
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);//setup curl connection with preferences
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);// we pass our 'chunk' struct to the callback function 
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
	 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");    //keep cookies in memory	
	 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); //don't verify peer against cert
	 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); //don't verify host against cert
	 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //bounce through login to next page
	 curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);   //mimic real world use
	 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);       //disable signals to use with threads

	//data we will be posting
     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    curl_easy_setopt(curl, CURLOPT_URL, "http://st189.lab.novell.com/ICSLogin/");
		//timestamp
		 ftime(&t1); //start
		res = curl_easy_perform(curl);
		 ftime(&t2); //stop
		char * url;  //url to verify the connection worked
		long respcode; //response code of the http transaction
		long redirect; //number of redirects after we connect
		char *ics ="ICSLogin"; //string to parse against to make sure it was successful
		curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL, &url); //grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &respcode);// grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_REDIRECT_COUNT, &redirect); //grabbing it from curl
		
		sec = t2.time - t1.time; //seconds
		msec = t2.millitm - t1.millitm; //milliseconds
		if (msec < 0) {			//adjust if fractional time < 0
			sec -= 1;
			msec += 1000;
		  }
		
		
		// if url is valid and respcode == 200 and redirect > 0
		 //if( (strstr(url, ics) == NULL)  && (respcode == 200) && (redirect > 0)){ 
		 if( (strstr(url, ics) == NULL)  && (respcode == 200)){
		  //printf("*");
		//printf("time to run=%ld.%ld and timestamp %ld tl=%ld\n", sec, msec, t1.time, totallogintime);
		counter++;
		addtotallogins(1);
		
		
	 //access a random # of pages
	int rand =max_req_client;
	int m;
	 for (m=0; m< rand; m++){
	
		 if(taskid==0 && (counter % 1000 == 0)){//limited output
			printf("logins=%ld req=%ld fail=%ld count =%d", totallogins, reqpass, reqfail, counter); 	
		 }
		curl_easy_setopt(curl, CURLOPT_URL, "http://st189.lab.novell.com/");
		 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);

		 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); //don't verify peer against cert
		 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); //don't verify host against cert
		 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //bounce through login to next page
		 curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);   //mimic real world use
		 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);       //disable signals to use with threads
		
		 ftime(&t1);//timestamp
		res = curl_easy_perform(curl);
		 ftime(&t2);
		curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL, &url); //grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &respcode);// grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_REDIRECT_COUNT, &redirect); //grabbing it from curl
		
		sec = t2.time - t1.time;
		msec = t2.millitm - t1.millitm;
		if (msec < 0) {			/*adjust if fractional time < 0*/
			sec -= 1;
			msec += 1000;
		  }
		
		 // if url is valid and respcode == 200 and redirect > 0
		
		if( (strstr(url, ics) == NULL)  && (respcode == 200) ){	
		//printf("+");
		//printf("time to run=%ld.%ld and timestamp %ld tl=%ld\n", sec, msec, t1.time, totallogintime);
		addtotalreqpass(1);
		counter++;
		  
		
		}
	    else {
			printf("ERROR LOGIN %d output %s url=%s response code=%d redirect count=%d\n", res, ics, url, respcode, redirect);
		  
		  
		  addtotalreqfail(1);
		  usleep(1000); //we received an error, sleep 100 ms and try again
		  counter++;
		}
		
		}//random # of pages 


	 }
                else {
                        //printf("ERROR REQ %d output %s url=%s response code=%d redirect count=%d\n", res, ics, url, respcode, redirect);
                  addtotalreqfail(1);
                  usleep(1000); //we received an error, sleep 100 ms and try again
                  counter++;
               }
	
      curl_easy_cleanup(curl); //cleanup curl information
	
	 }
	else { //error is curl initialization
			printf("error is curl initialization\n");
	}
	usleep(50);
	free(chunk.memory);
	chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
    	chunk.size = 0;    /* no data at this point */
  }//infinite while

  //should never reach here
	pthread_exit((void *) 0);
	 
 }	 
 
 void singlechild_create(){ 
	
	int taskid=0;
	int max_req_client =30;

	long sec, msec;
	struct MemoryStruct chunk;
    	chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
    	chunk.size = 0;    /* no data at this point */
	struct timeb t1,t2; //timing structure
	long counter =0;
	char *data="username=jeremy1&password=test";
   
while(1){
	
  if(ended()){
	  return;
	  //pthread_exit((void *) 0);
  }//stopping thread
	
     //new curl handle to restart the connection
	CURL *curl;
    CURLcode res;

	 curl = curl_easy_init();
    if(curl) { //continue if curl can be initialized
		
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);//setup curl connection with preferences
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);// we pass our 'chunk' struct to the callback function 
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
	 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");    //keep cookies in memory	
	 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); //don't verify peer against cert
	 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); //don't verify host against cert
	 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //bounce through login to next page
	 curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);   //mimic real world use
	 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);       //disable signals to use with threads

	//data we will be posting
     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    curl_easy_setopt(curl, CURLOPT_URL, "http://st189.lab.novell.com/ICSLogin/");
 
		//timestamp
		 ftime(&t1); //start
		res = curl_easy_perform(curl);
		 ftime(&t2); //stop
		char * url;  //url to verify the connection worked
		long respcode; //response code of the http transaction
		long redirect; //number of redirects after we connect
		char *ics ="ICSLogin"; //string to parse against to make sure it was successful
		curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL, &url); //grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &respcode);// grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_REDIRECT_COUNT, &redirect); //grabbing it from curl
		
		sec = t2.time - t1.time; //seconds
		msec = t2.millitm - t1.millitm; //milliseconds
		if (msec < 0) {			//adjust if fractional time < 0
			sec -= 1;
			msec += 1000;
		  }
		
		
		// if url is valid and respcode == 200 and redirect > 0
		 if( (strstr(url, ics) == NULL)  && (respcode == 200) && (redirect > 0)){ 
		 printf("*");
		//printf("time to run=%ld.%ld and timestamp %ld tl=%ld\n", sec, msec, t1.time, totallogintime);
		counter++;
		addtotallogins(1);
		
		
	 //access a random # of pages
	int rand =max_req_client;
	int m;
	 for (m=0; m< rand; m++){
	
		 if(taskid==0 && (counter % 10000 == 0)){//limited output
			printf("logins=%ld req=%ld fail=%ld count =%d", totallogins, reqpass, reqfail, counter); 	
		 }	
		curl_easy_setopt(curl, CURLOPT_URL, "http://st189.lab.novell.com/");
		curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);

		 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); //don't verify peer against cert
		 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); //don't verify host against cert
		 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //bounce through login to next page
		 curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);   //mimic real world use
		 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);       //disable signals to use with threads
		
		 ftime(&t1);//timestamp
		res = curl_easy_perform(curl);
		 ftime(&t2);
		curl_easy_getinfo(curl,CURLINFO_EFFECTIVE_URL, &url); //grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &respcode);// grabbing it from curl
		curl_easy_getinfo(curl,CURLINFO_REDIRECT_COUNT, &redirect); //grabbing it from curl
		
		sec = t2.time - t1.time;
		msec = t2.millitm - t1.millitm;
		if (msec < 0) {			/*adjust if fractional time < 0*/
			sec -= 1;
			msec += 1000;
		  }
		
		 // if url is valid and respcode == 200 and redirect > 0
		
		if( (strstr(url, ics) == NULL)  && (respcode == 200) ){	
		//printf("+");
		//printf("time to run=%ld.%ld and timestamp %ld tl=%ld\n", sec, msec, t1.time, totallogintime);
		addtotalreqpass(1);
		counter++;
		  
		
		}
	    else {
			printf("ERROR LOGIN %d output %s url=%s response code=%d redirect count=%d\n", res, ics, url, respcode, redirect);
		  
		  
		  addtotalreqfail(1);
		  usleep(1000); //we received an error, sleep 100 ms and try again
		  counter++;
		}
		
		}//random # of pages 


	 }
                else {
				  //printf("ERROR REQ %d output %s url=%s response code=%d redirect count=%d\n", res, ics, url, respcode, redirect);
                  addtotalreqfail(1);
                  usleep(1000); //we received an error, sleep 100 ms and try again
                  counter++;
               }
	
      curl_easy_cleanup(curl); //cleanup curl information
	
	 }
	else { //error is curl initialization
			printf("error is curl initialization\n");
	}
	usleep(50);
	free(chunk.memory);
	chunk.memory=NULL; /* we expect realloc(NULL, size) to work */
    	chunk.size = 0;    /* no data at this point */
  }//infinite while

  //should never reach here
 	return;	
 }	 
	

 
int main (int argc, char *argv[])
{
	
	//read command line arguments
	
	//read url file or make your own filenames
	
	
   //signal variable
	endedvar =0;
	
	struct sigaction new_action, old_action, ign_action;
     
       /* Set up the structure to specify the new action. */
       new_action.sa_handler = setended;
       sigemptyset (&new_action.sa_mask);
       new_action.sa_flags = 0;

       sigaction (SIGINT, NULL, &old_action);
       if (old_action.sa_handler != SIG_IGN)
         sigaction (SIGINT, &new_action, NULL);


	sem_init(&sem_reqpass, 0, 1);
	sem_init(&sem_reqfail, 0, 1);
	sem_init(&sem_totallogins, 0, 1);
	sem_init(&sem_timelogins, 0, 1);
	sem_init(&sem_timereqs, 0, 1);
	//sem_start, sem_reqpass, sem_reqfail, sem_logintime, sem_tlogins, sem_treq
	if(argc <= 1){
		printf("missing argument 1 for single or 2 for thread\n");
		 return 0;
	 }		  
	
	  //setup threads and run # of children
if(atoi(argv[1]) == 1){	  
	printf("single child started\n");
	
	struct timeb t1; ftime(&t1); //grab timestamp
	starttime = t1.time;	//save to starttime
	singlechild_create();   
	   
	ftime(&t1); 
   long runtime = t1.time - starttime;
   printf("runtime %ld total processes %ld total logins %ld\n", t1.time -starttime, reqpass+totallogins, totallogins);
   printf("requests valid=%ld request failed=%ld\n", reqpass, reqfail);
   if(runtime < 1) runtime =1;
   printf("requests per second = %ld\n",reqpass / runtime);
   pthread_exit(NULL);
	
	exit;   
	
}
else{
	
	int NUM_THREADS =10;
	struct thread_data data_array[NUM_THREADS];
   //struct thread_data data_array[1];
	pthread_t thread[NUM_THREADS];
	pthread_attr_t attr;
   int rc, t, status;

   // Initialize and set thread detached attribute 
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	
	
	
   for(t=0;t < NUM_THREADS;t++)
   {
	  data_array[t].threads =NUM_THREADS-1;
	  data_array[t].thread_id = t;
	  //data_array[t].exec = 10;
	  data_array[t].max_req_client = 10;
	  data_array[t].random = 0;
	  data_array[t].ssl = 0;
	  //data_array[t].domain = "st189.lab.novell.com";
	  //data_array[t].login = "jeremy1";
	  //data_array[t].password = "test";
	  data_array[1].uselogin =1;
      
	  rc = pthread_create(&thread[t], &attr, child_create, (void *) &data_array[t]);
      if (rc)
      {
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }
   printf("all threads created***************\n");
   //startthreads=0; //when all threads have started then being processing
   struct timeb t1; ftime(&t1); //grab timestamp
	starttime = t1.time;	//save to starttime

	
     // Free attribute and wait for the other threads 
   pthread_attr_destroy(&attr);
   
   int v;
   for(v=0;v < NUM_THREADS;v++)
   {	
       rc = pthread_join(thread[v], (void **)&status);
      if (rc)
      {
         printf("ERROR; return code from pthread_join() is %d\n", rc);
         //exit(-1);
      }
      printf("Completed join with thread %d status= %d\n",v, status);
   }
	ftime(&t1); 
   long runtime = t1.time - starttime;
   printf("runtime %ld total processes %ld total logins %ld\n", t1.time -starttime, reqpass+totallogins, totallogins);
   printf("requests valid=%ld request failed=%ld\n", reqpass, reqfail);
   if(runtime < 1) runtime =1;
   printf("requests per second = %ld\n",reqpass / runtime);
   pthread_exit(NULL);
}//else

    return 0;
  }
