From 1d46dd9b6064c3b1735df59d185eba89af3802d9 Mon Sep 17 00:00:00 2001
From: rahul
Date: Mon, 11 Nov 2019 11:50:55 +0530
Subject: socket dont-route & reuse-port, close prev-sock to reuse it

---
 src/ghdlserver/ghdlserver.c | 383 +++++++++++++++++++++-----------------------
 1 file changed, 182 insertions(+), 201 deletions(-)

(limited to 'src')

diff --git a/src/ghdlserver/ghdlserver.c b/src/ghdlserver/ghdlserver.c
index e93ae9f..e05a156 100644
--- a/src/ghdlserver/ghdlserver.c
+++ b/src/ghdlserver/ghdlserver.c
@@ -1,10 +1,14 @@
 /**********************************************************************************
  * <ghdlserver.c>  FOSSEE, IIT-Bombay
- * 15.Oct.2019 - Rahul Paknikar  - Switched to blocking sockets from non-blocking
+ **********************************************************************************
+ * 08.Nov.2019 - Rahul Paknikar  - Switched to blocking sockets from non-blocking
+ *								 - Close previous used socket to prevent from   
+ *								   generating too many socket descriptors
+ *								 - Enabled SO_REUSEPORT, SO_DONTROUTE socket options
  * 26.Sept.2019 - Rahul Paknikar - Added reading of IP from a file to 
  *                                 support multiple digital models
  *                               - On exit, the test bench removes the
- *                                 NGHDL_COMMON_IP file. It is shared by all the 
+ *                                 NGHDL_COMMON_IP_<ngspice_pid> file, shared by all
  *                                 nghdl digital models and is stored in /tmp
  *                                 directory. It tracks the used IPs for existing
  *                                 digital models in current simulation.
@@ -13,7 +17,8 @@
  *                                 a given event.
  *                               - Removed bug to terminate multiple testbench
  *                                 instances in ngpsice windows.
- *
+ **********************************************************************************
+ **********************************************************************************
  * 24.Mar.2017 - Raj Mohan - Added signal handler for SIGUSR1, to handle an 
  *                           orphan test bench process.
  *                           The test bench will now create a PID file in
@@ -60,16 +65,10 @@
 
 #define _XOPEN_SOURCE 500
 #define MAX_NUMBER_PORT 100
-
 #define NGSPICE "ngspice"     // 17.Mar.2017 - RM
 
-extern char* __progname;  // 26.Feb.2017 May not be portable to non-GNU systems.
-
-void Vhpi_Exit(int sig);
-
 static FILE* pid_file;
 static char pid_filename[80];
-
 static char* Out_Port_Array[MAX_NUMBER_PORT];           
 static int out_port_num = 0; 
 static int server_socket_id = -1;
@@ -77,6 +76,10 @@ static int sendto_sock;      // 22.Feb.2017 - RM - Kludge
 static int prev_sendto_sock; // 22.Feb.2017 - RM - Kludge
 static int pid_file_created; // 10.Mar.2017 - RM 
 
+extern char* __progname;  // 26.Feb.2017 May not be portable to non-GNU systems.
+
+void Vhpi_Exit(int sig);
+
 struct my_struct {
     char val[1024];                  
     char key[1024];       
@@ -85,6 +88,7 @@ struct my_struct {
 
 static struct my_struct *s, *users, *tmp = NULL;
 
+
 /* 17.Mar.2017 - RM - Get the process id of ngspice program.*/
 static int get_ngspice_pid(void)
 {
@@ -96,8 +100,8 @@ static int get_ngspice_pid(void)
 
     if ((dirp = opendir("/proc/")) == NULL)
     {
-	perror("opendir /proc failed");
-	exit(-1);
+		perror("opendir /proc failed");
+		exit(-1);
     }
 
     while ((dir_entry = readdir(dirp)) != NULL)
@@ -134,6 +138,7 @@ static int get_ngspice_pid(void)
    return(pid);
 }
 
+
 /* 23.Mar.2017 - RM - Pass the sock_port argument. We need this if a netlist
  * uses more than one instance of the same test bench, so that we can uniquely
  * identify the PID files.
@@ -143,16 +148,17 @@ static void create_pid_file(int sock_port)
 {
     pid_t my_pid = getpid();
     pid_t ngspice_pid = get_ngspice_pid();
+    
     if (ngspice_pid == -1)
     {
-      fprintf(stderr, "create_pid_file() Failed to get ngspice PID");
-      syslog(LOG_ERR,  "create_pid_file() Failed to get ngspice PID");
-      exit(1);
+      	fprintf(stderr, "create_pid_file() Failed to get ngspice PID");
+      	syslog(LOG_ERR,  "create_pid_file() Failed to get ngspice PID");
+      	exit(1);
     }
 
-    sprintf(pid_filename, "/tmp/NGHDL_%d_%s_%d", ngspice_pid, __progname, 
-            sock_port);
-    pid_file = fopen(pid_filename, "a");	// 26.Sept.2019 - RP
+    sprintf(pid_filename, "/tmp/NGHDL_%d_%s_%d", ngspice_pid, __progname, sock_port);
+    pid_file = fopen(pid_filename, "a");	// 26.Sept.2019 - RP - Open file in append mode
+    
     if (pid_file)
     {
     	pid_file_created = 1;
@@ -160,13 +166,12 @@ static void create_pid_file(int sock_port)
 	    fclose(pid_file);
     } else {
         perror("fopen() - PID file");
-	      syslog(LOG_ERR, "create_pid_file(): Unable to open PID file in /tmp");
+	    syslog(LOG_ERR, "create_pid_file(): Unable to open PID file in /tmp");
         exit(1);
     }
-
-    return;
 }
 
+
 #ifdef DEBUG
 static char* curtim(void)
 {
@@ -185,18 +190,18 @@ static char* curtim(void)
 }
 #endif
 
+
 #ifdef DEBUG
 static void print_hash_table(void) 
 {
     struct my_struct *sptr;
 
     for(sptr=users; sptr != NULL; sptr=sptr->hh.next)
-    {
-	syslog(LOG_INFO, "Hash table:val:%s: key: %s", sptr->val, sptr->key);
-    }
+		syslog(LOG_INFO, "Hash table:val:%s: key: %s", sptr->val, sptr->key);
 }
 #endif
 
+
 static void parse_buffer(int sock_id, char* receive_buffer)
 {
     static int rcvnum;
@@ -217,11 +222,11 @@ static void parse_buffer(int sock_id, char* receive_buffer)
         ptr1 = rest;
         while(var=strtok_r(token, ":", &value))
         {
-          s = (struct my_struct*)malloc(sizeof(struct my_struct));
-	  strncpy(s->key, var, 64);
-	  strncpy(s->val, value, 64);
-	  HASH_ADD_STR(users, key, s );
-	  break;    
+          	s = (struct my_struct*)malloc(sizeof(struct my_struct));
+	  		strncpy(s->key, var, 64);
+	  		strncpy(s->val, value, 64);
+	  		HASH_ADD_STR(users, key, s );
+	  		break;    
         }
     }
         
@@ -231,60 +236,69 @@ static void parse_buffer(int sock_id, char* receive_buffer)
     HASH_ADD_STR(users, key, s);
 }
 
+
 //Create Server and listen for client connections.
 // 26.Sept.2019 - RP - added parameter of socket ip
 static int create_server(int port_number, char my_ip[], int max_connections)
 {
- int sockfd, reuse = 1;
- struct sockaddr_in serv_addr;
-
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
-
- if (sockfd < 0)
- {
-    fprintf(stderr, "%s- Error: in opening socket on port %d\n", 
-            __progname, port_number);
-    exit(1);
- }
-
-/* 20.Mar.2017 - RM - SO_REUSEADDR option. To take care of TIME_WAIT state.*/
- int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
- if (ret < 0) 
- {
-    syslog(LOG_ERR, "create_server:setsockopt() failed....");
- }
-
- bzero((char *) &serv_addr, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = inet_addr(my_ip);  // 26.Sept.2019 - RP - Bind to specific IP only
- serv_addr.sin_port = htons(port_number);
+ 	int sockfd, reuse = 1;
+ 	struct sockaddr_in serv_addr;
+
+ 	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ 	if (sockfd < 0)
+ 	{
+    	fprintf(stderr, "%s- Error: in opening socket at server \n", __progname);
+    	//exit(1);
+    	return -1;
+ 	}
+
+	/* 20.Mar.2017 - RM - SO_REUSEADDR option. To take care of TIME_WAIT state.*/
+ 	int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
+
+ 	/* 08.Nov.2019 - RP - SO_REUSEPORT and SO_DONTROUTE option.*/
+ 	ret += setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
+  	ret += setsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE, &reuse, sizeof(int));
+ 
+ 	if (ret < 0)
+ 	{
+    	syslog(LOG_ERR, "create_server:setsockopt() failed....");
+    	// close(sockfd);
+    	// return -1;
+ 	}
+
+ 	bzero((char *) &serv_addr, sizeof(serv_addr));
+ 	serv_addr.sin_family = AF_INET;
+ 	serv_addr.sin_addr.s_addr = inet_addr(my_ip);  // 26.Sept.2019 - RP - Bind to specific IP only
+ 	serv_addr.sin_port = htons(port_number);
      
- if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
- {
-     fprintf(stderr,"%s- Error: could not bind socket to port %d\n",
+ 	if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
+ 	{
+     	fprintf(stderr,"%s- Error: could not bind socket to port %d\n",
              __progname, port_number);
-     syslog(LOG_ERR, "Error: could not bind socket to port %d", port_number);
-     close(sockfd);
-     exit(1);
- }
+     	syslog(LOG_ERR, "Error: could not bind socket to port %d", port_number);
+     	close(sockfd);
+     	exit(1);
+ 	}
 
- // Start listening on the server.
- listen(sockfd, max_connections);
+ 	// Start listening on the server.
+	listen(sockfd, max_connections);
 
- return sockfd;
+	return sockfd;
 }
 
+
 // The server to wait (blocking) for a client connection.
 static int connect_to_client(int server_fd)                                            
 {                                                                               
     int ret_val = 0;
     int newsockfd = -1;
     socklen_t clilen;
-    struct sockaddr_in  cli_addr;
+    struct sockaddr_in cli_addr;
     
     clilen = sizeof(cli_addr); 
 
-    /* 15.Oct.2019 - RP - Blocking Socket (Accept) */
+    /* 08.Nov.2019 - RP - Blocking Socket (Accept) */
     newsockfd = accept(server_fd, (struct sockaddr *) &cli_addr, &clilen);
     if (newsockfd >= 0)
     { 
@@ -296,85 +310,72 @@ static int connect_to_client(int server_fd)
 	    exit(1);
     }                   
 
-    return(newsockfd);
+    return newsockfd;
 }   
 
+
 //Receive string from socket and put it inside buffer.
-static int receive_string(int sock_id, char* buffer)                                   
+static void receive_string(int sock_id, char* buffer)                                   
 {                                                                               
-  int nbytes = 0;
-  int ret;  
+  	int nbytes = 0;
 
-	/* 15.Oct.2019 - RP - Blocking Socket - Receive */    
+	/* 08.Nov.2019 - RP - Blocking Socket - Receive */    
     nbytes = recv(sock_id, buffer, MAX_BUF_SIZE, 0);
-
-    if (nbytes < 0)
+    if (nbytes <= 0)
     {
 		perror("READ FAILURE");
         exit(1);
     }
+}
 
-    return(nbytes); 
-}   
 
 static void Data_Send(int sockid)                                       
-{                                                                               
-  static int trnum;
-  char* out;
-
-  int i;
-  char colon = ':';
-  char semicolon = ';'; 
-  int wrt_retries = 0;
-  int ret;
-
-  s = NULL;
-  int found = 0;
-
-  out = calloc(1, 2048);
-
-  // 5.July.2019 - RP - loop to send all ports at once for an event
-  for (i=0; i<out_port_num; i++)  
-  {  
-     
-     found = 0;
-     
-     HASH_FIND_STR(users,Out_Port_Array[i],s);
-     if (strcmp(Out_Port_Array[i], s->key) == 0) 
-     {
-      found=1;
-     }
-
-      if(found) 
-      { 
-          strncat(out, s->key, strlen(s->key));
-          strncat(out, &colon, 1);
-          strncat(out, s->val, strlen(s->val));
-          strncat(out, &semicolon, 1);
-      }         
-      else                                                                        
-      {        
-
-          syslog(LOG_ERR,"The %s's value not found in the table.",
+{                                                                              
+  	static int trnum;
+  	char* out;
+
+  	int i;
+  	char colon = ':';
+  	char semicolon = ';'; 
+  	int wrt_retries = 0;
+  	int ret;
+
+  	s = NULL;
+
+  	out = calloc(1, 2048);
+
+  	// 5.July.2019 - RP - loop to send all ports at once for an event
+  	for (i=0; i<out_port_num; i++)  
+  	{  
+     	HASH_FIND_STR(users,Out_Port_Array[i],s);
+     	if (strcmp(Out_Port_Array[i], s->key) == 0) 
+     	{
+      		strncat(out, s->key, strlen(s->key));
+          	strncat(out, &colon, 1);
+          	strncat(out, s->val, strlen(s->val));
+          	strncat(out, &semicolon, 1);
+      	}      
+      	else                                                                        
+      	{        
+          	syslog(LOG_ERR,"The %s's value not found in the table.",
                  Out_Port_Array[i]);
-          free(out);
-          return;
-      }
-
+          	free(out);
+          	return;
+     	}
     }
 
-    /* 15.Oct.2019 - RP - Blocking Socket (Send) */
-      if ((send(sockid, out, strlen(out), 0)) == -1)
-        {
-          syslog(LOG_ERR,"Failure sending to CLT:%d buffer:%s", sockid, out);
-          exit(1);
-        }
+    /* 08.Nov.2019 - RP - Blocking Socket (Send) */
+    if ((send(sockid, out, strlen(out), 0)) == -1)
+    {
+        syslog(LOG_ERR,"Failure sending to CLT:%d buffer:%s", sockid, out);
+        exit(1);
+    }
 
-      syslog(LOG_INFO,"SNT:TRNUM:%d to CLT:%d buffer: %s", trnum++, sockid, out);  
-      free(out);
-    
+    syslog(LOG_INFO,"SNT:TRNUM:%d to CLT:%d buffer: %s", trnum++, sockid, out);  
+    free(out);
 } 
 
+
 // 26.Sept.2019 - RP - added parameter of socket ip
 void Vhpi_Initialize(int sock_port, char sock_ip[])
 {
@@ -382,41 +383,37 @@ void Vhpi_Initialize(int sock_port, char sock_ip[])
 
     signal(SIGINT,Vhpi_Exit);
     signal(SIGTERM,Vhpi_Exit);
-
     signal(SIGUSR1, Vhpi_Exit); //10.Mar.2017 - RM
 
     int try_limit = 100;
 
     while(try_limit > 0)
     {
+      	// 26.Sept.2019 - RP
+      	server_socket_id = create_server(DEFAULT_SERVER_PORT, sock_ip, DEFAULT_MAX_CONNECTIONS);
 
-      // 26.Sept.2019 - RP
-      server_socket_id = create_server(DEFAULT_SERVER_PORT, sock_ip, DEFAULT_MAX_CONNECTIONS);
-      if(server_socket_id > 0)
-        {
+      	if(server_socket_id >= 0)
+      	{
            syslog(LOG_INFO,"Started the server on port %d  SRV:%d",
 		  DEFAULT_SERVER_PORT, server_socket_id);
             break;
         }
-        else
-		{
-            syslog(LOG_ERR,"Could not start server on port %d,will try again",
+        
+        syslog(LOG_ERR,"Could not start server on port %d,will try again",
                    DEFAULT_SERVER_PORT);
-		    usleep(1000);
-		    try_limit--;
+	    usleep(1000);
+	    try_limit--;
 	        
-		    if(try_limit==0)
-		    {
-		       syslog(LOG_ERR,
-	                 "Error:Tried to start server on port %d, failed..giving up.",
+	    if(try_limit==0)
+	    {
+	      	syslog(LOG_ERR,
+	           "Error:Tried to start server on port %d, failed..giving up.",
 	                      DEFAULT_SERVER_PORT);
-		       exit(1);
-            }
-		}
+		    exit(1);
+        }
     }
-  //                                                                            
-  //Reading Output Port name and storing in Out_Port_Array;                     
-  //                                                                            
+                                                                              
+  	//Reading Output Port name and storing in Out_Port_Array;
     char* line = NULL;
     size_t len = 0; 
     ssize_t read;
@@ -425,105 +422,89 @@ void Vhpi_Initialize(int sock_port, char sock_ip[])
     struct timespec ts;
 
     fp=fopen("connection_info.txt","r");
-    if (! fp)
+    if (!fp)
     {
-	syslog(LOG_ERR,"Vhpi_Initialize: Failed to open connection_info.txt. Exiting...");
-	exit(1);
+		syslog(LOG_ERR,"Vhpi_Initialize: Failed to open connection_info.txt. Exiting...");
+		exit(1);
     }
 
     line = (char*) malloc(80);
     while ((read = getline(&line, &len, fp)) != -1)
     {
-	if (strstr(line,"OUT") != NULL || strstr(line,"out") != NULL )
-	{ 
-	    strtok_r(line, " ",&token);
-	    Out_Port_Array[out_port_num] = line;
-	    out_port_num++;
-	}
-	line = (char*) malloc(80);
+		if (strstr(line,"OUT") != NULL || strstr(line,"out") != NULL)
+		{ 
+		    strtok_r(line, " ",&token);
+		    Out_Port_Array[out_port_num] = line;
+		    out_port_num++;
+		}
+		line = (char*) malloc(80);
     }                     	
     fclose(fp);
-    
     free(line);
 
     ts.tv_sec = 2;
     ts.tv_nsec = 0;
     nanosleep(&ts, NULL);
 
-// 10.Mar.2017 - RM - Create PID file for the test bench.
+	// 10.Mar.2017 - RM - Create PID file for the test bench.
     create_pid_file(sock_port);
 }
+
+
 void Vhpi_Set_Port_Value(char *port_name,char *port_value,int port_width)
 {
-    
-  s = (struct my_struct*)malloc(sizeof(struct my_struct));
-  strncpy(s->key, port_name,64);
-  strncpy(s->val,port_value,64);
-  HASH_ADD_STR( users, key, s );
-
+	s = (struct my_struct*)malloc(sizeof(struct my_struct));
+	strncpy(s->key, port_name,64);
+	strncpy(s->val,port_value,64);
+	HASH_ADD_STR( users, key, s );
 }
 
+
 void Vhpi_Get_Port_Value(char* port_name,char* port_value,int port_width)
 {
-
-  HASH_FIND_STR(users,port_name,s);
-  if(s)
-  {  
-    snprintf(port_value,sizeof(port_value),"%s",s->val);
-
-    HASH_DEL(users, s);
-    free(s);
-    s=NULL;
-  }
+  	HASH_FIND_STR(users,port_name,s);
+  	if(s)
+  	{  
+    	snprintf(port_value,sizeof(port_value),"%s",s->val);
+    	HASH_DEL(users, s);
+    	free(s);
+    	s=NULL;
+  	}
 }
 
+
 void Vhpi_Listen()
 {
-    int new_sock;
+    sendto_sock = connect_to_client(server_socket_id);	// 22.Feb.2017 - RM - Kludge
+	char receive_buffer[MAX_BUF_SIZE];
+	receive_string(sendto_sock, receive_buffer);
+    
+   	syslog(LOG_INFO, "Vhpi_Listen:New socket connection CLT:%d",sendto_sock);
 
-    while(1)
+   	if(strcmp(receive_buffer, "END")==0) 
     {
-		new_sock = connect_to_client(server_socket_id);
+        syslog(LOG_INFO, "RCVD:CLOSE REQUEST from CLT:%d", sendto_sock);  
+    	Vhpi_Exit(0);
+    }  
 
-        if(new_sock  > 0) 
-        {
-            char receive_buffer[MAX_BUF_SIZE];
-	    	    int n = receive_string(new_sock, receive_buffer);
-    	    	if(n > 0)
-            {
-      				sendto_sock = new_sock; // 22.Feb.2017 - RM - Kludge
-      				syslog(LOG_INFO, "Vhpi_Listen:New socket connection CLT:%d",new_sock);
-
-      				if(strcmp(receive_buffer, "END")==0) 
-              {
-                syslog(LOG_INFO, "RCVD:CLOSE REQUEST from CLT:%d", new_sock);  
-                Vhpi_Exit(0);
-              }  
-  	      		else 
-              {
-                parse_buffer(new_sock,receive_buffer);
-              }
-              break;
-            }
-        } 
-    	  else
-      	{
-        	break;
-      	}
-    }
+	parse_buffer(sendto_sock, receive_buffer);    
 }
 
+
 void  Vhpi_Send() 
 {
 // 22.Feb.2017 - RM - Kludge
     if (prev_sendto_sock != sendto_sock)
     { 
-	    Data_Send(sendto_sock);                                      
+	    Data_Send(sendto_sock);
+
+	    close(prev_sendto_sock);	// 08.Nov.2019 - RP - Close previous socket
 	    prev_sendto_sock = sendto_sock;
     }
 // 22.Feb.2017 End kludge 
 }
 
+
 void Vhpi_Exit(int sig) 
 {                          
     close(server_socket_id);
-- 
cgit