/******************************************************************************
版权所有：	
文件名称：	net_maintain.c
文件版本：	01.01
创建作者：	xxxxxx
创建日期：	2013-03-05
功能说明：	网络维护接口
其它说明：
修改记录：
*/
/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"
#ifdef LINUX_KERNEL_APP
#include <linux/delay.h> 
#else
#include <pthread.h>
#include <sys/prctl.h>
#endif
/*------------------------------- 宏定义 --------------------------------------
*/


/*------------------------------ 类型结构 -------------------------------------
*/


/*------------------------------ 全局变量 -------------------------------------
*/
#ifdef LINUX_KERNEL_APP
static struct task_struct * g_ts_maintain;
#else
static pthread_t g_ts_maintain = 0;
#endif
static SOCKET g_maintain_client = RT_SOCKET_ERR;
u32	g_ip_net_maintain;	// 104维护工具调试客户端地址

unsigned char g_net_miantain_buf[NET_RECV_MAX];  // 以太网接收缓冲区
static volatile int g_net_miantain_sock_flag = NET_SOCKET_INIT;//关闭SOCKET标志

#ifdef LINUX_KERNEL_APP
static pid_t g_ts_maintain_pid ;
#endif
static int g_net_maintain_exit_flag = 0;
static int 	port_openflag=0; //端口服务打开标志


int g_mt_zombie_flag;
//端口服务选择.127表示启动最后一个网络的ftp ,telnet 端口服务
//128启动所有网络的ftp ,telnet 端口服务
char g_port_server_sel[]={'1', '2', '8', 0}; 

/*------------------------------ 函数声明 -------------------------------------
*/
int _net_maintain_thread(void * unused);


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/

//设置socket关闭标志并等待socket关闭完成
void net_maintain_set_sock_closing(int no)
{
	struct sockaddr_in local_addr;
	unsigned int len = sizeof(local_addr);
	RUN_PARA *pRunPara=&tRunPara;
	unsigned int ip=0;
	int bflag = 0,count=0;

	//如果有客户端连接
	if(g_maintain_client != RT_SOCKET_ERR)
	{
		//获取客户端连接的IP
		rt_getsockname(g_maintain_client, (struct sockaddr *)&local_addr,  &len);

		if(pRunPara->tNetPara[no].old_index==0)
		{
			//获取网卡IP
			rt_if_ip_get(pRunPara->tNetPara[no].old_ethid, (unsigned char *)&ip);//sunxi: 20220825 335x
		}
		else
		{
			//获取虚拟网卡上的IP地址
			rt_if_ip_get2(pRunPara->tNetPara[no].old_ethid, pRunPara->tNetPara[no].old_index, (unsigned char *)&ip);//sunxi: 20220825 335x
		}

		//如果是该网卡的IP并且该网卡要修改IP或MASK,则需要通知关闭socket
		if((htonl (ip)==local_addr.sin_addr.s_addr) && pRunPara->tNetPara[no].bInit)//sunxi: 20220825 335x
		{
			bflag = 1;
		
		}		
			
		//该网卡的IP和MASK没有修改,则不需要通知关闭socket
		if(!bflag)
		{
			return;
		}
			
		g_net_miantain_sock_flag = NET_SOCKET_CLOSING; //置关闭标志
		
		//等待关闭完成
		while(g_net_miantain_sock_flag != NET_SOCKET_CLOSED)
		{
			if(++count>150)
			{
				rt_printf("net miantain socket close failed!\r\n");
				break;
			}

			msleep(20);
		}		
	}

}

//设置socket重新创建标志
void net_maintain_set_sock_initflag(void)
{

	if(g_net_miantain_sock_flag == NET_SOCKET_CLOSED)
	{
		g_net_miantain_sock_flag = NET_SOCKET_REINIT;	

	}
	
}

//   TODO: 需要确认是否需要重新初始化
#ifdef LINUX_KERNEL_APP
int net_maintain_init(void)
{
	struct sched_param sp;
	pid_t pid;

	// 如果已经初始化，退出
	if(g_ts_maintain)
	{
		return 0;
	}

	// 运行应用调试线程
	//g_ts_maintain = kthread_run(_net_maintain_thread,NULL,"net_maintain");
	pid = kernel_thread(_net_maintain_thread, NULL,CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
	g_ts_maintain = find_task_by_vpid(pid);	
	if(IS_ERR(g_ts_maintain))
	{
		printk("ts=%p\r\n",g_ts_maintain);
		g_ts_maintain = NULL;
		return -1;
	}

	g_ts_maintain_pid = pid;
	// 设置调度策略和优先级
	sp.sched_priority = 9;
	sched_setscheduler(g_ts_maintain,SCHED_FIFO,&sp);
	// 设置进程名字
	set_task_comm(g_ts_maintain, "net_maintain");		

	return 0;
}
#else
int net_maintain_init(void)
{
#if 1
    int net=0;
    int ret;
    pthread_attr_t attr;
    char buf[32];
    // 如果已经初始化，退出
    if(g_ts_maintain)
    {
        return 0;
    }

    // 运行应用104线程
    sprintf(buf,"maintain%d",net);

    // 设置调度策略和优先级
    pthread_attr_init(&attr);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);  //调度
    struct sched_param s_parm;
    //s_parm.sched_priority = sched_get_priority_max(SCHED_FIFO);
    s_parm.sched_priority = 9;
    pthread_attr_setschedparam(&attr, &s_parm);  //优先级

    //attr.__schedpolicy = SCHED_FIFO;
    //attr.__schedparam.sched_priority = 8;

    ret = pthread_create(&g_ts_maintain, &attr, (void *)_net_maintain_thread, NULL);
    if(!ret)
    {
        rt_printf("net = %d, ret = %d, err = %s\r\n", net, ret, strerror(ret));
    }

	return 0;
#endif
}

#endif

#ifdef LINUX_KERNEL_APP
int net_maintain_exit(void)
{
	int status;
	if(g_ts_maintain != NULL)
	{
		g_net_maintain_exit_flag = 1;
		sys_wait4(g_ts_maintain_pid, (int __user *)&status, 0, NULL);

		g_ts_maintain =NULL;
	}

	return 0;
}
#else
 int net_maintain_exit(void)
 {
	 //int status;
 
	 if(g_ts_maintain)
	 {
		 g_net_maintain_exit_flag = 1;
		 // sys_wait4(g_ts_maintain_pid, (int __user *)&status, 0, NULL);
		 pthread_join(g_ts_maintain, NULL);
 
		 g_ts_maintain = 0;
	 }
 
	 return 0;
 }
#endif

#ifdef LINUX_KERNEL_APP
 void net_maintain_zombie(void)
 {
	int status;
	
 	if(g_mt_zombie_flag)
 	{
 		// 关闭端口服务
		net_debug_exit();
		close_inet_port();
		
	 	if(g_ts_maintain != NULL && g_mt_zombie_flag == 1)
		{
			sys_wait4(g_ts_maintain_pid, (int __user *)&status, 0, NULL);
			g_ts_maintain =NULL;
		}
		
 		g_mt_zombie_flag = 0;
 	}
 }
#else
 void net_maintain_zombie(void)
 {
	//int status;
	
	if(g_mt_zombie_flag)
	{
		// 关闭端口服务
		net_debug_exit();
	   
		close_inet_port();
 
		if(g_ts_maintain && g_mt_zombie_flag == 1)
		{
			// sys_wait4(g_ts_maintain_pid, (int __user *)&status, 0, NULL);
			pthread_join(g_ts_maintain, NULL);
			g_ts_maintain = 0;
		}
		
		g_mt_zombie_flag = 0;
	}
 }
 
#endif


int net_maintain_send(unsigned char *buf,int len)
{
	if(g_maintain_client == RT_SOCKET_ERR)
	{
		return 0;
	}

	return	rt_send(g_maintain_client,buf,len,0);
}

#ifdef CPU_FUXI
void open_inet_port(void)
{
	// char *envp[] = { NULL };
	// char *cmd[] = {"/etc/init.d/inetd", "start",  NULL};

	if(port_openflag)
	{
		rt_printf("open_inet_port =%d fail, return!!\r\n",port_openflag);
		return;
	}

	port_openflag = 1;

	//调用shell脚本,打开对应端口	
	system("/etc/init.d/S70vsftpd start");
	// call_usermodehelper("/etc/init.d/inetd",  cmd,  envp,  UMH_NO_WAIT);
}
#else
void open_inet_port(void)
{
	#if 0
	char *envp[] = { NULL };
	char *cmd[] = {"/usr/sbin/inetd", "-q", g_port_server_sel,  NULL};
	int ret ;

	if(port_openflag)
	{
		return;
	}
	
	port_openflag = 1;
	ret = get_mt_port_proc( );
	if(ret==NET_MT_PORT_PROC_2)
	{
		strcpy(g_port_server_sel, "127"); 
	}
	else
	{
		strcpy(g_port_server_sel, "128"); 
	
	}
	//调用shell脚本,打开对应端口
	call_usermodehelper("/usr/sbin/inetd",  cmd,  envp,  UMH_WAIT_PROC);	
	#else
	char *envp[] = { NULL };
	char *cmd[] = {"/usr/sbin/vsftpd", "/etc/vsftpd.conf &",  NULL};
	if(port_openflag)
	{
		return;
	}

	port_openflag = 1;
	//调用shell脚本,打开对应端口
	call_usermodehelper("/usr/sbin/vsftpd",  cmd,  envp,  UMH_WAIT_PROC);
	#endif


}
#endif

#ifdef CPU_FUXI
void close_inet_port(void)
{
	char *envp[] = { NULL };
	char *cmd0[] = {"/usr/bin/killall", "telnetd",  NULL}; // sunxi 20220727 added
	char *cmd1[] = {"/usr/bin/killall", "vsftpd",  NULL}; // sunxi 20220822 added
	// char *cmd[] = {"/etc/init.d/inetd", "stop",  NULL};

	if(!port_openflag)
	{
		return;
	}	

	port_openflag = 0;
	
	//调用shell脚本,关闭对应端口
	call_usermodehelper("/usr/bin/killall",  cmd0,  envp,  UMH_WAIT_PROC); // sunxi 20220727 added
	call_usermodehelper("/usr/bin/killall",  cmd1,  envp,  UMH_WAIT_PROC); // sunxi 20220822 added
	// call_usermodehelper("/etc/init.d/inetd",  cmd,  envp,  UMH_WAIT_PROC);
}
#else
void close_inet_port(void)
{
	char *envp[] = { NULL };


	char *cmd0[] = {"/usr/bin/killall", "telnetd",  NULL}; // sunxi 20220727 added
	char *cmd1[] = {"/usr/bin/killall", "vsftpd",  NULL}; // sunxi 20220822 added
	char *cmd[] = {"/etc/rc.d/init.d/inetd", "stop",  NULL};

	if(!port_openflag)
	{
		return;
	}	

	port_openflag = 0;
	
	//调用shell脚本,关闭对应端口
	call_usermodehelper("/usr/bin/killall",  cmd0,  envp,  UMH_WAIT_PROC); // sunxi 20220727 added
	call_usermodehelper("/usr/bin/killall",  cmd1,  envp,  UMH_WAIT_PROC); // sunxi 20220822 added
	call_usermodehelper("/etc/rc.d/init.d/inetd",  cmd,  envp,  UMH_WAIT_PROC);
}
#endif

//得到ENV_MT_PORT_ON的处理方法
// 0：表示应用启动时关闭除2404外的所有服务端口.如果维护工具连接，在所有网络上启动需要的服务端口，
//	  保证现场运行的装置可以通过主站升级程序。出货用。
// 1：表示任何时候都不关闭任何服务端口。特殊情况用。
// 2：表示应用启动时关闭除2404外的所有服务端口.如果维护工具连接，只在网络3上启动需要的服务端口，
//    这样在正常连主站的网络扫描不到任何端口，以保证测试不问题。测试用。
int get_mt_port_proc(void)
{

	char *mt_port = env_get_info(ENV_MT_PORT_ON);

	if(!mt_port)
	{
		return NET_MT_PORT_PROC_0;
	}

	if(strcmp(mt_port, "1")==0)//表示应用启动时不关闭服务端口.如果启动服务端口,可以在任意网口上启动
	{
		return NET_MT_PORT_PROC_1;
	}
	else if(strcmp(mt_port, "2")==0)//应用启动时关闭服务端口.如果启动服务端口,只在最后一个网口上启动
	{
		return NET_MT_PORT_PROC_2;
	}

	return NET_MT_PORT_PROC_0;

}


/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/
int _net_maintain_thread(void * unused)
{
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
#ifndef LINUX_KERNEL_APP
	struct timeval timeout;
#endif
	struct linger  ling;

	SOCKET maintain_server,tmp_sock; 
	
	int sin_size;
	int i,len,ret;
	unsigned char *ip;
#ifdef LINUX_KERNEL_APP	
	unsigned int recv_timeout;
	unsigned int link_timeout = dTCounter;
#else
	int recv_timeout;
	int link_timeout = 0;
#endif	
	unsigned int ip_server;

	prctl(PR_SET_NAME, "_net_maintain_t");
	while(1)
	{
	
		g_ip_net_maintain = 0;  //清零
		// 1、建立socket并侦听
		// 服务器端开始建立socket描述符
#ifdef LINUX_KERNEL_APP		
		maintain_server = rt_socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
#else
		maintain_server = rt_socket(AF_INET, SOCK_STREAM, 0);
#endif
		if (maintain_server == RT_SOCKET_ERR)
		{
			RT_PRINTF_POSITION();
			g_ts_maintain = NULL;
			return -1;
		}

		// 服务器端填充sockaddr结构
		memset (&server_addr, 0, sizeof (struct sockaddr_in));
		server_addr.sin_family = AF_INET;
		if(get_mt_port_proc()==NET_MT_PORT_PROC_2)
		{
			if(rt_if_ip_get2(0,  3, (unsigned char *)&ip_server)==0)
			{
				server_addr.sin_addr.s_addr = htonl (ip_server);	
			}
			else
			{
				server_addr.sin_addr.s_addr = htonl (INADDR_ANY);
			}
		}
		else
		{
			server_addr.sin_addr.s_addr = htonl (INADDR_ANY);
		}
		server_addr.sin_port = htons (NET_MAINTAIN_PORT);

		// 捆绑sockfd描述符
		ret = rt_bind (maintain_server, (struct sockaddr *)(&server_addr), sizeof (struct sockaddr));
		if (ret < 0)
		{
			RT_PRINTF_POSITION();
			rt_socket_close (maintain_server);
			g_ts_maintain = NULL;
			
			return -2;
		}

#ifndef LINUX_KERNEL_APP
		// 设置接收超时
		timeout.tv_sec  = 0;
		timeout.tv_usec = 500000;
		rt_setsockopt(maintain_server,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout ,sizeof(struct timeval));
#endif	
		// 设置关闭延时
		ling.l_onoff = 1;
		ling.l_linger = 0;
		rt_setsockopt(maintain_server,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger));
		
		// 监听sockfd描述符
		ret = rt_listen (maintain_server, 1);
		if (ret < 0)
		{
			RT_PRINTF_POSITION();
			rt_socket_close (maintain_server);
			g_ts_maintain = NULL;
			
			return -3;
		}

		g_maintain_client = RT_SOCKET_ACCEPT_RST;
		
		// 2、接受外部链接
		#ifdef RT_SOCKET_FD
		while(g_maintain_client < 0)
		#else
		while(g_maintain_client == RT_SOCKET_ACCEPT_RST)
		#endif
		{
			msleep(100);	
			
			// 服务器阻塞，直到客户程序建立连接
			sin_size = sizeof (struct sockaddr_in);
			g_maintain_client = rt_accept (maintain_server, (struct sockaddr*)(&client_addr), &sin_size);

			// 检查是否退出
			if(g_net_maintain_exit_flag)
			{
				// 关闭客户端
				if(g_maintain_client)
				{
					tmp_sock = g_maintain_client;
					g_maintain_client = RT_SOCKET_ERR;
					rt_socket_close (tmp_sock);
				}

				
				// 关闭服务器
				rt_socket_close (maintain_server);

				g_net_maintain_exit_flag = 0;
				return 0;
			}
			
			//IP已经修改要关闭SOCKET
			if(g_net_miantain_sock_flag == NET_SOCKET_CLOSING)
			{
				//关闭客户端
				if(g_maintain_client != RT_SOCKET_ERR)
				{
					tmp_sock = g_maintain_client;
					g_maintain_client = RT_SOCKET_ERR;
					rt_socket_close (tmp_sock);
				}

				
				// 关闭服务器
				rt_socket_close (maintain_server);	

				//置已经关闭标志
				g_net_miantain_sock_flag = NET_SOCKET_CLOSED;
				//等待IP修改完成
				while(g_net_miantain_sock_flag != NET_SOCKET_REINIT)
				{
					msleep(100);	
				}
				//置为初始化
				g_net_miantain_sock_flag = NET_SOCKET_INIT;
				goto NET_END;
	
			}
			
			
		}

		// 3、关闭侦听socket，避免多个外部连接
		rt_socket_close(maintain_server);

		ip = (unsigned char *)&client_addr.sin_addr.s_addr;
		rt_printf("maintain client connect(ip=%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]);
		{
			char buf[64];
			char log_info[128];
			sprintf(buf,"6008端口连接(%d#网络:IP=%d.%d.%d.%d)!\r\n", set_get_network_id_from_socket(g_maintain_client)+1, ip[0],ip[1],ip[2],ip[3]);
			log_str_time(LOG_OPERATE,buf,0,1);

			sprintf(log_info,"6008 port connection(%d#net:IP=%d.%d.%d.%d)",set_get_network_id_from_socket(g_maintain_client)+1, ip[0],ip[1],ip[2],ip[3]);
			load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,1);
		}
		
		//记录客户端地址
		memcpy(&g_ip_net_maintain, ip, 4);
		
		// 4、客户端已连接，处理客户端命令
#ifdef LINUX_KERNEL_APP		
		recv_timeout=dTCounter;
#else
		recv_timeout=0;
#endif
		while(1)
		{
#ifdef LINUX_KERNEL_APP		
			msleep(2);//noted by sunxi: 20221212 设为非阻塞型
			len = rt_recv (g_maintain_client, g_net_miantain_buf, NET_RECV_MAX, MSG_DONTWAIT);
#else			
			len = rt_recv (g_maintain_client, g_net_miantain_buf, NET_RECV_MAX, 0);
#endif

			// 检查是否退出
			if(g_net_maintain_exit_flag)
			{
				// 关闭客户端
				tmp_sock = g_maintain_client;
				g_maintain_client = RT_SOCKET_ERR;
				rt_socket_close (tmp_sock);

				g_net_maintain_exit_flag = 0;
				return 0;
			}

			//IP已经修改要关闭SOCKET
			if(g_net_miantain_sock_flag == NET_SOCKET_CLOSING)
			{
				// 关闭客户端
				tmp_sock = g_maintain_client;
				g_maintain_client = RT_SOCKET_ERR;
				rt_socket_close (tmp_sock);

				//置已经关闭标志
				g_net_miantain_sock_flag = NET_SOCKET_CLOSED;
				//等待IP修改完成
				while(g_net_miantain_sock_flag != NET_SOCKET_REINIT)
				{
					msleep(100);	
				}
				//置为初始化
				g_net_miantain_sock_flag = NET_SOCKET_INIT;
				goto NET_END;

			}	
			
#ifdef CPU_FUXI
			if((len == -EAGAIN)||(len==-1))//noted by sunxi: 这里要加-1, 不然连接不上，可能与上面的rt_recv有关
#else
			if(len == -EAGAIN)
#endif
			{
				if(rt_get_net_linkstatus(rt_get_netcard_id_from_socket(g_maintain_client))==1)
				{
#ifdef LINUX_KERNEL_APP				
					if((dTCounter - link_timeout)>RT_LINK_TIMEOUT*T_1s) //500ms超时
#else
					link_timeout++;
					if(link_timeout>RT_LINK_TIMEOUT*2) //500ms超时
#endif					
					{
						rt_printf("%s: link down\n",__FUNCTION__);
#ifdef LINUX_KERNEL_APP						
						link_timeout  = dTCounter;
#else
						link_timeout  = 0;
#endif
						break;	
					}
				}
				else
				{
#ifdef LINUX_KERNEL_APP				
					link_timeout  = dTCounter;
#else
					link_timeout  = 0;
#endif

				}
				// 超时
#ifdef LINUX_KERNEL_APP				
				if((dTCounter -  recv_timeout)>120*T_1s)  // 超时2分钟，端口未收到数据，关闭端口
#else
				recv_timeout++;
				if(recv_timeout>240)  // 超时2分钟，端口未收到数据，关闭端口
#endif				
				{
					rt_printf ("net_maintain_thread:timeout\r\n");
				
					break;
				}				
				continue;
			}
			else if (len <= 0)
			{
				//调用recv时，如果返回0，则表示对端关闭；返回负数，表示出错
				rt_printf ("net_maintain_thread:peer close(ret=%d).\r\n",len);
				msleep(100);	
				break;
			}

			// 接收数据
#ifdef LINUX_KERNEL_APP			
			recv_timeout = dTCounter;
			link_timeout  = dTCounter;
#else
			recv_timeout=0;
			link_timeout  = 0;
#endif			
			for(i=0;i<len;i++)
			{
				Maintain_Recv(g_net_miantain_buf[i]);
			}
		}

		// 关闭客户端socket
		tmp_sock = g_maintain_client;
		g_maintain_client = RT_SOCKET_ERR;
		{
			char buf[64];
			char log_info[128];
			sprintf(buf,"6008端口关闭(%d#网络:IP=%d.%d.%d.%d)!\r\n", set_get_network_id_from_socket(tmp_sock)+1, ip[0],ip[1],ip[2],ip[3]);
			log_str_time(LOG_OPERATE,buf,0,0);
			sprintf(log_info,"6008 port close(%d#net:IP=%d.%d.%d.%d)",set_get_network_id_from_socket(tmp_sock)+1, ip[0],ip[1],ip[2],ip[3]);
			load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,0);
		}
		rt_socket_close (tmp_sock);

NET_END:
		// 如果维护端口打开环境变量不存在，直接退出
		if(get_mt_port_proc() != 1)
		{
			// 设置僵死标志，让主循环回收
			g_mt_zombie_flag = 1;
			break;
		}
		else
		{
			g_mt_zombie_flag = 0;
		}
	}

	
	return 0;
}

/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/


/*------------------------------ 文件结束 -------------------------------------
*/
