
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <memory.h>
#include <signal.h>
#include <unistd.h>
#include "pgLibTunnel.h"


static unsigned int s_uIsNormal = 0;


static void DebugLogOut(unsigned int uLevel, const char* lpszOut)
{
	if (s_uIsNormal) {
		fprintf(stdout, "%s\n", lpszOut);
	}
}

static void InitService(unsigned int uIsNormal)
{
	if (uIsNormal) {
		fprintf(stdout, "pgTunnelStatic: Init service: run as normal\n");
	}
	else {
		signal(SIGTTOU, SIG_IGN);
		signal(SIGTTIN, SIG_IGN);
		signal(SIGTSTP, SIG_IGN);
		signal(SIGHUP, SIG_IGN);

		int iPid = fork();
		if (iPid < 0) {
			fprintf(stderr, "pgTunnelStatic: Init service: fork failed: %s\n", strerror(errno));    
			exit(1);
		}
		else if (iPid != 0) {
			exit(0);
		}

		if (setsid() < 0) {
			fprintf(stderr, "pgTunnelStatic: Init service: setsid failed: %s\n", strerror(errno));
			exit(1);
		}

		iPid = fork();
		if (iPid < 0) {
			fprintf(stderr, "pgTunnelStatic: Init service: second fork failed: %s\n", strerror(errno));    
			exit(1);
		}
		else if (iPid != 0) {
			exit(0);
		}

		char szCurDirPath[256] = {0};
		int iReadSize = readlink("/proc/self/exe", szCurDirPath, sizeof(szCurDirPath));
		if (iReadSize > 0) {
			char *pszTemp = strrchr(szCurDirPath, '/');
			if (pszTemp != 0) {
				pszTemp[0] = '\0';
			}
		}
		if (szCurDirPath[0] == '\0') {
			szCurDirPath[0] = '/';
			szCurDirPath[1] = '\0';
		}

		if (chdir(szCurDirPath) < 0) {
			fprintf(stderr, "pgTunnelStatic: Init service: chdir failed: %s\n", strerror(errno));    
			exit(1);
		}

		// Close all files.
		int fd, fdtablesize;
		for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) {
			close(fd);
		}

		// Clean file masks.
		umask(0);

		fprintf(stdout, "pgTunnelStatic: Init service: sucess\n");    

		// Igorne signal.
		signal(SIGINT, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
		signal(SIGTTOU, SIG_IGN);
		signal(SIGTTIN, SIG_IGN);
		signal(SIGTSTP, SIG_IGN);
		signal(SIGHUP, SIG_IGN);
		signal(SIGABRT, SIG_IGN);
		signal(SIGALRM, SIG_IGN);
		signal(SIGPIPE, SIG_IGN);
		signal(SIGUSR1, SIG_IGN);
		signal(SIGUSR2, SIG_IGN);
		signal(SIGVTALRM, SIG_IGN);
		//signal(SIGTERM, SIG_IGN);
	}
} 

static void ReplaceDevID(char* lpszSysInfo, char* lpszDevID)
{
	if (lpszDevID[0] == '\0') {
		return;
	}

	char* pszTemp = strstr(lpszSysInfo, "(DevID){");
	if (pszTemp == 0) {
		return;
	}

	pszTemp += strlen("(DevID){");
	char* pszTemp1 = strchr(pszTemp, '}');
	if (pszTemp1 == 0) {
		return;
	}

	unsigned int uDevIDSize = strlen(lpszDevID);
	unsigned int uDevIDSize1 = pszTemp1 - pszTemp;
	if (uDevIDSize != uDevIDSize1) {
		memmove((pszTemp + uDevIDSize), pszTemp1, strlen(pszTemp1));
	}

	memcpy(pszTemp, lpszDevID, uDevIDSize);
}

static void StrTrim(char* lpszStr)
{
	char* pszTemp0 = lpszStr;
	while ((*pszTemp0) != '\0') {
		if (strchr(" \r\n\t", (*pszTemp0)) == 0) {
			break;
		}
		pszTemp0++;
	}

	char* pszTemp1 = lpszStr + strlen(lpszStr);
	while (pszTemp1 > pszTemp0) {
		if (strchr(" \r\n\t", (*(pszTemp1 - 1))) == 0) {
			break;
		}
		pszTemp1--;
	}
	
	int iSize = (pszTemp1 - pszTemp0);
	memmove(lpszStr, pszTemp0, iSize);
	lpszStr[iSize] = '\0';
}

static unsigned int LoadDevID(const char* lpszCfgFile, char* lpszDevID, unsigned int uSize)
{
	unsigned int uRet = 0;

	FILE* pFile = fopen(lpszCfgFile, "r");
	if (pFile != 0) {
		while (1) {
			char szLine[256] = {0};
			if (fgets(szLine, sizeof(szLine), pFile) == 0) {
				break;
			}

			StrTrim(szLine);

			if (strstr(szLine, "id=") == szLine) {
				unsigned int uSizeLine = strlen(szLine);
				unsigned int uSizeTag = strlen("id=");
				if ((uSizeLine - uSizeTag) < uSize) {
					strcpy(lpszDevID, &(szLine[uSizeTag]));
					StrTrim(lpszDevID);
					uRet = 1;
				}
				break;
			}
		}

		fclose(pFile);
	}

	return uRet;
}


int main(int argc, char* argv[])
{
	// Initialize service.
	s_uIsNormal = (argc > 1) ? (unsigned int)atoi(argv[1]) : 0;
	InitService(s_uIsNormal);

	// Build ID file name.
	char szIdFile[256] = {0};
	sprintf(szIdFile, "%s.id", argv[0]);

	// Load device id from file.
	char szDevID[128] = {0};
	if (!LoadDevID(szIdFile, szDevID, sizeof(szDevID))) {
		fprintf(stdout, "pgTunnelStatic: Load device id from file failed!\n");
		return 0;
	}

	// Build config file name.
	char szCfgFile[256] = {0};
	sprintf(szCfgFile, "%s.cfg", argv[0]);

	// Build system info.
	char szSysInfo[256] = {0};
	if (pgTunnelSysInfo(szCfgFile, szSysInfo, sizeof(szSysInfo)) != PG_TUNNEL_ERROR_OK) {
		fprintf(stdout, "pgTunnelStatic: Build system info failed!\n");
		sprintf(szSysInfo, "(DevID){0}");
	}

	// Replace the device id.
	ReplaceDevID(szSysInfo, szDevID);

	// Start tunnel.
	int iErr = pgTunnelStart(szCfgFile, szSysInfo, 0, DebugLogOut);
	if (iErr != PG_TUNNEL_ERROR_OK) {
		fprintf(stdout, "pgTunnelStatic: Tunnel start failed, Error=%d\n", iErr);
		return 0;
	}

	// Pending the main thread.
	while (1) {
		usleep(10 * 1000 * 1000);
	}
	
	// Stop tunnel.
	pgTunnelStop();
	return 0;
}

