Переглянути джерело

MySql-Info implementiert.

Rind 4 роки тому
батько
коміт
3a5c535169

+ 1 - 0
README.md

@@ -28,6 +28,7 @@ gfa/shm.h, gfa/mutex.h, gfa/sema.h, gfa/procmem.h
 * Funktionen zur Ermittlung von Prozess- und Speicherinformationen
 * Funktionen zur Ermittlung von CPU-Informationen
 * Funktionen zur Überwachung von Hotplug-Events von Mass Storage Devices (z. B. USB oder MMC)
+* Funktionen zur Ermittlung von MySql-Server Statusinformationen. 
 * Exportiert C++ Synchronisationsobjekte wie Mutex und Semaphore.
 * Hilfsfunktionen zur UUID-Manipulation
 

+ 7 - 1
gfaipc.pro

@@ -15,6 +15,7 @@ SOURCES += \
     src/shm.cpp \
     src/shmrot.cpp \
     src/sysinfo.cpp \
+    src/thread.cpp \
     src/uuid.c
 
 HEADERS += \
@@ -28,6 +29,7 @@ HEADERS += \
     src/shm.h \
     src/shmrot.h \
     src/sysinfo.h \
+    src/thread.h \
     src/uuid.h
 
 ####################################################################################
@@ -65,6 +67,7 @@ linux-g++ {
 	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/procmem.h $(INSTALL_ROOT)$$includes.path
 	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/uuid.h $(INSTALL_ROOT)$$includes.path
 	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/ipcpriv.h $(INSTALL_ROOT)$$includes.path
+	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/thread.h $(INSTALL_ROOT)$$includes.path
 	includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfaipc.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/mutex.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/sema.h
@@ -72,6 +75,7 @@ linux-g++ {
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/procmem.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/uuid.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/ipcpriv.h
+	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/thread.h
 	INSTALLS += includes
 }
 
@@ -98,6 +102,7 @@ linux-buildroot-g++ {
 		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/procmem.h $(INSTALL_ROOT)$$includes.path
 		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/uuid.h $(INSTALL_ROOT)$$includes.path
 		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/ipcpriv.h $(INSTALL_ROOT)$$includes.path
+		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/thread.h $(INSTALL_ROOT)$$includes.path
 		includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfaipc.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/mutex.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/sema.h
@@ -105,6 +110,7 @@ linux-buildroot-g++ {
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/procmem.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/uuid.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/ipcpriv.h
+		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/thread.h
 		INSTALLS += includes
 
 		itoolchain.target = install_toolchain
@@ -127,7 +133,7 @@ deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET="$(TARGET)" >> deployt
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET0="$(TARGET0)" >> deploytargets
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET1="$(TARGET1)" >> deploytargets
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET2="$(TARGET2)" >> deploytargets
-deploylib.commands += $$escape_expand(\\n\\t)@echo HEADERS="\\\"gfaipc.h mutex.h sema.h shm.h procmem.h uuid.h ipcpriv.h\\\"" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo HEADERS="\\\"gfaipc.h mutex.h sema.h shm.h procmem.h uuid.h thread.h ipcpriv.h\\\"" >> deploytargets
 PRE_TARGETDEPS += deploylib
 QMAKE_EXTRA_TARGETS += deploylib
 

+ 14 - 4
gfasysinfo/gfasysinfo.pro

@@ -11,11 +11,11 @@ CONFIG(debug, debug|release) {
     QMAKE_CFLAGS -= -Os
     QMAKE_CXXFLAGS += -D_DEBUG
     QMAKE_CFLAGS += -D_DEBUG
-	QMAKE_LIBS += -lgfaipcd -lgfasitarautilsd -pthread
+	QMAKE_LIBS += -lgfaipcd -lgfasitarautilsd -pthread -lmysqlclient
 }
 
 CONFIG(release, debug|release) {
-	QMAKE_LIBS += -lgfaipc -lgfasitarautils -pthread
+	QMAKE_LIBS += -lgfaipc -lgfasitarautils -pthread -lmysqlclient
 }
 
 linux-buildroot-g++ {
@@ -26,6 +26,16 @@ linux-buildroot-g++ {
 }
 
 SOURCES += \
-    src/main.cpp
+    ../src/procmem.cpp \
+    src/main.cpp \
+    src/mysqlinfo.cpp \
+    src/mysqlwrap.cpp \
+    src/procfile.cpp \
+    src/stgdevinfo.cpp
 
-HEADERS +=
+HEADERS += \
+    ../src/procmem.h \
+    src/mysqlinfo.h \
+    src/mysqlwrap.h \
+    src/procfile.h \
+    src/stgdevinfo.h

+ 67 - 266
gfasysinfo/src/main.cpp

@@ -17,9 +17,11 @@
 #include <libudev.h>
 #include <poll.h>
 #include <errno.h>
+#include <getopt.h>
 #include <gfa/gfasitarautils.h>
-#include "../../src/gfaipc.h"
-#include "../../src/ipcpriv.h"
+#include <gfa/gfaipc.h>
+#include "stgdevinfo.h"
+#include "mysqlinfo.h"
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -34,28 +36,10 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-#define _BYTE_SIZE_KIB						(1ULL << 10) // Kibibyte
-#define _BYTE_SIZE_MIB						(1ULL << 20) // Mebibyte
-#define _BYTE_SIZE_GIB						(1ULL << 30) // Gibibyte
-#define _BYTE_SIZE_TIB						(1ULL << 40) // Tebibyte
-#define _BYTE_SIZE_PIB						(1ULL << 50) // Pebibyte
-#define _BYTE_SIZE_EIB						(1ULL << 60) // Exbibyte
-
 #define _APPID								GFA_APPCTRL_APPID_SYSINFO
 #define _APPNAME							"SysInfo"
 #define _CYCLE_INTV							500
 
-#define _STR_EQUALS(s, t)					(!!s && !strcmp(s, t))
-
-#define _MOUNTS_FILE						"/proc/mounts"
-#define _INTERNAL_EMMC_DISK					"mmcblk1"
-#define _INTERNAL_EMMC_PART1				"mmcblk1p1"
-#define _INTERNAL_EMMC_PART2				"mmcblk1p2"
-
-/////////////////////////////////////////////////////////////////////////////
-
-typedef std::map<std::string, std::string>	MountMap;
-
 /////////////////////////////////////////////////////////////////////////////
 
 static volatile bool						g_fRun		= false;
@@ -76,7 +60,7 @@ static const char *g_pszStateNames[] =
 };
 
 /////////////////////////////////////////////////////////////////////////////
-
+#if 0
 static long long	_NumberFromString(const char *pszString, int base = 10, bool *pbErr = NULL);
 static const char*	_ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, char *pszValue, size_t nCChValue, bool bTruncate = false);
 static long long	_ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, int base = 10, bool *pbErr = NULL);
@@ -140,14 +124,6 @@ static int _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, boo
 	return nIndex;
 }
 
-#if 0
-static int _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode, bool &bChange)
-{
-	int nIndex = _LookupPartition(sdm, pszDevNode);
-	return _RemovePartition(sdm, nIndex, bChange);
-}
-#endif
-
 static bool _PartitionSetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx, int nDiskIdx, bool &bChange)
 {
 	bChange = false;
@@ -252,14 +228,6 @@ static int _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, bool &bC
 	return nIndex;
 }
 
-#if 0
-static int _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode, bool &bChange)
-{
-	int nIndex = _LookupDisk(sdm, pszDevNode);
-	return _RemoveDisk(sdm, nIndex, bChange);
-}
-#endif
-
 static unsigned int _DiskAddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nDiskIdx, int nPartIdx, bool &bChange)
 {
 	bChange = false;
@@ -465,10 +433,11 @@ static bool _ProcessMounts(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm)
 
 	return bChange;
 }
+#endif
 
 /////////////////////////////////////////////////////////////////////////////
 
-static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
+static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI, bool &bStateTransition)
 {
     ctrlmsg_t nCtrlMsg;
 
@@ -477,12 +446,14 @@ static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
 		switch(nCtrlMsg)
 		{
 		case GFA_APPCTRL_CTRLMSG_STOP:
+			bStateTransition = g_fRun;
 			g_fRun = false;
 			TRACE("Received Message: STOP!\n");
 			break;
 		case GFA_APPCTRL_CTRLMSG_PAUSE:
 			if(!g_fPause)
 			{
+				bStateTransition = true;
 				g_fPause = true;
 				::GfaIpcAppCtrlSetState(hAC, GIAS_Paused);
 				TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Paused]);
@@ -491,6 +462,7 @@ static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
 		case GFA_APPCTRL_CTRLMSG_RESUME:
 			if(g_fPause)
 			{
+				bStateTransition = true;
 				g_fPause = false;
 				::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
 				TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Running]);
@@ -502,6 +474,7 @@ static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
 	}
 }
 
+#if 0
 static const char* _GetFileSpec(const char *pszPathname)
 {
 	if(pszPathname && *pszPathname)
@@ -527,137 +500,6 @@ static bool _IsInternalEmmc(const char *pszDevNode)
 	return false;
 }
 
-#if 0
-static void _EnumDevProperties(struct udev_device *dev, int nIndent)
-{
-	if(dev)
-	{
-		struct udev_list_entry *prop;
-		struct udev_list_entry *props = ::udev_device_get_properties_list_entry(dev);
-
-		if(props)
-		{
-			udev_list_entry_foreach(prop, props)
-			{
-				const char *pszName = ::udev_list_entry_get_name(prop);
-				const char *pszValue = ::udev_list_entry_get_value(prop);
-
-				if(pszName && pszValue)
-				{
-					TRACE("%-*s+ %s = \"%s\"\n", nIndent + 2, " ", pszName, pszValue);
-				}
-			}
-		}
-	}
-}
-
-static void _EnumDevSysAttributes(struct udev_device *dev, int nIndent)
-{
-	if(dev)
-	{
-		struct udev_list_entry *att;
-		struct udev_list_entry *atts = ::udev_device_get_sysattr_list_entry(dev);
-		if(atts)
-		{
-			udev_list_entry_foreach(att, atts)
-			{
-				const char *pszName = ::udev_list_entry_get_name(att);
-				const char *pszValue = ::udev_device_get_sysattr_value(dev, pszName);
-
-				if(pszName && pszValue && strcmp(pszName, "uevent"))
-				{
-					TRACE("%-*s- %s = \"%s\"\n", nIndent + 2, " ", pszName, pszValue);
-				}
-			}
-		}
-	}
-}
-#endif
-
-static const char *_FormatByteSize(unsigned long long nCb, char *pszBuf, size_t nCChBuf)
-{
-	if(pszBuf && nCChBuf)
-	{
-		int nPrec = 2;
-		double val = nCb;
-		
-		if(nCb < _BYTE_SIZE_KIB)
-		{
-			snprintf(pszBuf, nCChBuf, "%llu B", nCb); // Byte
-		}
-		else if(nCb < _BYTE_SIZE_MIB)
-		{
-			val /= _BYTE_SIZE_KIB;
-			snprintf(pszBuf, nCChBuf, "%.*f KiB", nPrec, val); // KiB
-		}
-		else if(nCb < _BYTE_SIZE_GIB)
-		{
-			val /= _BYTE_SIZE_MIB;
-			snprintf(pszBuf, nCChBuf, "%.*f MiB", nPrec, val); // MiB
-		}
-		else if(nCb < _BYTE_SIZE_TIB)
-		{
-			val /= _BYTE_SIZE_GIB;
-			snprintf(pszBuf, nCChBuf, "%.*f GiB", nPrec, val); // GiB
-		}
-		else if(nCb < _BYTE_SIZE_PIB)
-		{
-			val /= _BYTE_SIZE_TIB;
-			snprintf(pszBuf, nCChBuf, "%.*f TiB", nPrec, val); // TiB
-		}
-		else if(nCb < _BYTE_SIZE_EIB)
-		{
-			val /= _BYTE_SIZE_PIB;
-			snprintf(pszBuf, nCChBuf, "%.*f PiB", nPrec, val); // PiB
-		}
-		
-		return pszBuf;
-	}
-	
-	return NULL;
-}
-
-static void _DumpDeviceMap(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
-{
-	TRACE("\n");
-	for(size_t i = 0; i < _countof(sdm.disks); ++i)
-	{
-		char szByteBuf[16];
-		const GFA_SYSINFO_DISK &disk = sdm.disks[i];
-
-		if(disk.valid && !disk.internal)
-		{
-			TRACE("Disk:\n");
-			TRACE("  |-Label:  %s\n", disk.szName);
-			TRACE("  |-Vendor: %s\n", *disk.szVendor ? disk.szVendor : "n/a");
-			TRACE("  |-Node:   %s\n", disk.szDevNode);
-			TRACE("  |-Type:   %s\n", *disk.szBus ? disk.szBus : "mmc");
-
-			for(unsigned int j = 0; j < disk.nPartCount; ++j)
-			{
-				int nIndex = disk.aPartIdx[j];
-				const GFA_SYSINFO_PARTITION &part = sdm.parts[nIndex];
-				bool bMounted = !!*part.szMntPoint;
-
-				TRACE("  |-Partition:\n");
-				TRACE("    |-Label:    %s\n", part.szFsLabel);
-				TRACE("    |-Node:     %s\n", part.szDevNode);
-				TRACE("    |-PA-Size:  %s\n", _FormatByteSize(part.nKiBPartSize * _BYTE_SIZE_KIB, szByteBuf, sizeof(szByteBuf)));
-				TRACE("    |-Mount:    %s\n", bMounted ? part.szMntPoint : "Not mounted");
-				if(bMounted)
-				{
-					TRACE("    |-FS-Type:  %s\n", part.szFsType);
-					TRACE("    |-FS-Ver.:  %s\n", part.szFsVersion);
-					TRACE("    |-FS-Size:  %s\n", _FormatByteSize(part.nKiBSize * _BYTE_SIZE_KIB, szByteBuf, sizeof(szByteBuf)));
-					TRACE("    |-FS-Used:  %s\n", _FormatByteSize(part.nKiBUsed * _BYTE_SIZE_KIB, szByteBuf, sizeof(szByteBuf)));
-					TRACE("    |-FS-Free:  %s\n", _FormatByteSize(part.nKiBFree * _BYTE_SIZE_KIB, szByteBuf, sizeof(szByteBuf)));
-				}
-			}
-		}
-	}
-	TRACE("\n");
-}
-
 static long long _NumberFromString(const char *pszString, int base, bool *pbErr)
 {
 	if(!pszString || !*pszString)
@@ -841,6 +683,8 @@ static void _EnumStorageDevices(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &m
 
 	::udev_enumerate_unref(pEnum);
 }
+#endif
+
 /////////////////////////////////////////////////////////////////////////////
 
 static void _SigHandler(int sig)
@@ -857,33 +701,38 @@ static void _SigHandler(int sig)
 
 int main(int argc, char *argv[])
 {
-	UNUSED(argc);
-	UNUSED(argv);
-
+	int c;
 	HAPPCTRL hAC = NULL;
 	HAPPINFO hAI;
-	CCycleTimer ct(_CYCLE_INTV);
+	CCycleTimer ct(_CYCLE_INTV), perfCnt(0U);
 	cy_time_t wStart, wEnd, wCur = 0;
-	struct udev *pUdev = NULL;
-	struct timeval tv;
-	memset(&tv, 0, sizeof(tv));
-	struct udev_monitor *pUdevMon = NULL;
-	GFA_SYSINFO_STORAGE_DEVICE_MAP sdm;
-	memset(&sdm, 0, sizeof(sdm));
-	MountMap mm;
+	std::string sDbUser, sDbPass;
+	CMySqlInfo mySqlInfo;
+	CStgDevInfo stgDevInfo;
+	bool bStateTransition;
 
-	pollfd pfd[2];
-	memset(&pfd, 0, sizeof(pfd));
-
-	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
+	// parse command line options
+
+	while((c = getopt(argc, argv, "p:u:")) != -1)
+	{
+		switch(c)
+		{
+		case 'p':
+			sDbPass = optarg;
+			break;
+		case 'u':
+			sDbUser = optarg;
+			break;
+		}
+	}
+
 	/////////////////////////////////////////////////////////////////////////
 	// signal handling
 
 	struct sigaction sa;
 	memset(&sa, 0, sizeof(sa));
 
-	// handle signals
 	sa.sa_handler = _SigHandler;
     sigaction(SIGHUP, &sa, NULL);	// handles user's terminal disconnect
     sigaction(SIGQUIT, &sa, NULL);	// handles Ctrl + '\'
@@ -897,8 +746,6 @@ int main(int argc, char *argv[])
     sigaction(SIGCHLD, &sa, NULL);	// ignores child process termination
     sigaction(0, &sa, NULL);		// ignores shell termination
 
-	/////////////////////////////////////////////////////////////////////////
-	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
 	// initialize
 
@@ -920,62 +767,35 @@ int main(int argc, char *argv[])
 			break;
 	    }
 
-	    if(!(pUdev = ::udev_new()))
-	    {
-	        TRACE("udev_new failed\n");
-			break;
-	    }
-
-		_UpdateMountMap(mm);
-		_EnumStorageDevices(sdm, mm, pUdev);
-
-		if(!(pUdevMon = ::udev_monitor_new_from_netlink(pUdev, "udev")))
-		{
-	        TRACE("udev_monitor_new_from_netlink failed\n");
-			break;
-		}
-
-		if(::udev_monitor_filter_add_match_subsystem_devtype(pUdevMon, "block", "partition") < 0)
-		{
-	        TRACE("udev_monitor_filter_add_match_subsystem_devtype failed\n");
-			break;
-		}
-
-		if(::udev_monitor_enable_receiving(pUdevMon) < 0)
-		{
-	        TRACE("udev_monitor_enable_receiving failed\n");
-			break;
-		}
-
-		pfd[0].fd = ::udev_monitor_get_fd(pUdevMon);
-		pfd[0].events = POLLIN;
-		pfd[0].revents = 0;
-		pfd[1].fd = open(_MOUNTS_FILE, O_RDONLY, 0);
-		pfd[1].events = POLLPRI;
-		pfd[1].revents = 0;
-
-		;;GfaIpcDumpSHMROT();
-
+		::GfaIpcDumpSHMROT();
+		
 		TRACE("My Name:  %s\n", _APPNAME);
 		TRACE("My AppID: %llu\n", _APPID);
 		TRACE("My PID:   %d\n", getpid());
 		TRACE("My Cycle: %d\n", _CYCLE_INTV);
 
+		CMySqlInfo::EXEC_PARAMS myep = {hAC, sDbUser, sDbPass};
+		mySqlInfo.Create(&myep);
+		
+		CStgDevInfo::EXEC_PARAMS step = {hAC};
+		stgDevInfo.Create(&step);
+
 		g_fZombie = false;
 		g_fRun = true;
 		::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
-		::GfaIpcAppCtrlUpdateStorageDeviceMap(hAC, sdm);
-//		_DumpDeviceMap(sdm);
+		mySqlInfo.Signal(CMySqlInfo::S_UpdateAll);
+		stgDevInfo.Signal(CStgDevInfo::S_Init);
 	}
 	while(false);
 
-	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
 	// run
 
 	while(g_fRun)
 	{
+		bStateTransition = false;
+
 		/////////////////////////////////////////////////////////////////////
 		// trigger cycle timer
 
@@ -986,68 +806,49 @@ int main(int argc, char *argv[])
 
 		if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, wCur)))
 		{
-			_ProcessCtrlMessages(hAC, hAI);
+			_ProcessCtrlMessages(hAC, hAI, bStateTransition);
 		}
 
 		/////////////////////////////////////////////////////////////////////
 		// if not paused, do work
-
-		if(!g_fPause && g_fRun)
+		
+		if(g_fRun)
 		{
-			wStart = ct.GetMicroTick();
-			::GfaIpcAppCtrlUpdateSysInfo(hAC);
-			wEnd = ct.GetMicroTick();
-			wCur = wEnd - wStart;
-
-			_ClearMapChanges(sdm);
-
-			while(poll(pfd, 2, _CYCLE_INTV / 5) > 0)
+			if(bStateTransition)
 			{
-				_UpdateMountMap(mm);
-
-				if(pfd[0].revents & POLLIN)
+				if(g_fPause)
 				{
-					struct udev_device* dev;
-
-					while((dev = ::udev_monitor_receive_device(pUdevMon)))
-					{
-						_ProcessPartition(sdm, mm, dev);
-					}
+					mySqlInfo.Signal(CMySqlInfo::S_Pause);
+					stgDevInfo.Signal(CStgDevInfo::S_Pause);
 				}
-
-				if(pfd[1].revents & POLLPRI)
+				else
 				{
-					_ProcessMounts(sdm, mm);
+					mySqlInfo.Signal(CMySqlInfo::S_Resume);
+					stgDevInfo.Signal(CStgDevInfo::S_Resume);
 				}
 			}
 
-			if(_DeviceMapChanged(sdm))
+			if(!g_fPause)
 			{
-				::GfaIpcAppCtrlUpdateStorageDeviceMap(hAC, sdm);
-//				_DumpDeviceMap(sdm);
+				wStart = ct.GetMicroTick();
+				::GfaIpcAppCtrlUpdateSysInfo(hAC);
+				wEnd = ct.GetMicroTick();
+				wCur = wEnd - wStart;
 			}
-		}
-
-		/////////////////////////////////////////////////////////////////////
-		// if running, sleep
 
-		if(g_fRun)
 			ct.Sleep1();
+		}
 	}
 
-	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////
 	// terminate
 
-	if(pfd[0].fd >= 0)
-		close(pfd[0].fd);
-	if(pfd[1].fd >= 0)
-		close(pfd[1].fd);
-	if(pUdevMon)
-		::udev_monitor_unref(pUdevMon);
-	if(pUdev)
-		::udev_unref(pUdev);
+	mySqlInfo.Signal(CMySqlInfo::S_Terminate);
+	stgDevInfo.Signal(CStgDevInfo::S_Terminate);
+
+	mySqlInfo.Join(NULL);
+	stgDevInfo.Join(NULL);
 	
 	if(g_fZombie)
 	{

+ 563 - 0
gfasysinfo/src/mysqlinfo.cpp

@@ -0,0 +1,563 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <gfa/gfasitarautils.h>
+#include "mysqlinfo.h"
+#include "mysqlwrap.h"
+#include "procfile.h"
+#include "../../src/procmem.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef _DEBUG
+#define TRACE(...)							fprintf(stdout, __VA_ARGS__), fflush(stdout)
+#else	//	_DEBUG
+#define TRACE(...)
+#endif	//	_DEBUG
+
+#define UNUSED(v)							(void)v
+#define _countof(a)							(sizeof(a) / sizeof(*a))
+
+#define _TIMESPEC_2_US(ts)					(((clock64_t)(ts).tv_sec) * 1000000LL + ((clock64_t)(ts).tv_nsec) / 1000LL)
+#define _MYSQL_PROCESS_NAME					"mysqld"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _MYSQL_HOST							"localhost"
+#define _QUICK_UPDATE_INTERVAL_MS			3000LL
+#define _SLOW_UPDATE_INTERVAL_MS			(4LL * _QUICK_UPDATE_INTERVAL_MS)
+#define _LAZY_UPDATE_INTERVAL_MS			(5LL * _SLOW_UPDATE_INTERVAL_MS)
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _IsIgnoredDb(const char *pszDb)
+{
+	static const char *pszIgnoredDBs[] = {"information_schema", "mysql", "test"};
+
+	for(size_t i = 0; i < _countof(pszIgnoredDBs); ++i)
+	{
+		if(!strcmp(pszDb, pszIgnoredDBs[i]))
+			return true;
+	}
+
+	return false;
+}
+
+static std::string & _rtrim(std::string &s, const char *sep)
+{
+	std::size_t f = s.find_last_not_of(sep);
+	if(f != std::string::npos)
+		s.erase(f + 1);
+	return s;
+}
+
+static CMySqlVar _QuerySingleServerVariable(CMySqlDB &db, const char *pszVarName)
+{
+	char sql[256];
+	sprintf(sql, "SHOW VARIABLES WHERE `Variable_Name` = '%s';", pszVarName);
+	CMySqlResult res = db.Query(sql);
+	if(!res.error())
+	{
+		CMySqlRow row;
+		if(res.FetchRow(row))
+			return row["Value"];
+	}
+
+	return CMySqlVar();
+}
+
+static CMySqlVar _QuerySingleGlobalStatusValue(CMySqlDB &db, const char *pszValName)
+{
+	char sql[256];
+	sprintf(sql, "SHOW GLOBAL STATUS WHERE `Variable_name` = '%s';", pszValName);
+	CMySqlResult res = db.Query(sql);
+	if(!res.error())
+	{
+		CMySqlRow row;
+		if(res.FetchRow(row))
+			return row["Value"];
+	}
+
+	return CMySqlVar();
+}
+
+static pid_t _ReadPid(const char *pszPidFilePath)
+{
+	std::ifstream fPid(pszPidFilePath);
+	std::string str;
+
+	if(fPid.good())
+	{
+		str.assign((std::istreambuf_iterator<char>(fPid)), std::istreambuf_iterator<char>());
+		return (pid_t)atoi(str.c_str());
+	}
+
+	return (pid_t)-1;
+}
+
+static clock64_t _GetHeartbeatUs(void)
+{
+	struct timespec	ts;
+	::clock_gettime(CLOCK_MONOTONIC, &ts);
+	return _TIMESPEC_2_US(ts);
+}
+
+static unsigned long long _GetDirectoryDiscUsage(const char *pszDirName)
+{
+	if(!pszDirName || !*pszDirName)
+		return false;
+	char szCmd[PATH_MAX];
+	sprintf(szCmd, "du -sk %s", pszDirName);
+	FILE *pf = popen(szCmd, "r");
+	if(pf)
+	{
+		unsigned long long nSize = 0;
+		char szLine[32];
+		fgets(szLine, sizeof(szLine), pf);
+		pclose(pf);
+
+		if(sscanf(szLine, "%llu", &nSize) == 1)
+		{
+			return nSize * 1024ULL;
+		}
+	}
+	return 0;
+}
+
+static bool _GetAppTimes(pid_t &pid, const char *pszProcName, GFA_APPCTRL_APPTIMES &at)
+{
+	CProcPidStatFile ppsf;
+	static double fUtimeOld = 0.0, fStimeOld = 0.0;
+	static clock64_t nHeartbeatOld = 0, nStartTime = 0;
+
+	if(pid)
+	{
+		if(!ppsf.ReadFile(pid))
+		{
+			fUtimeOld = fStimeOld = 0.0;
+			nHeartbeatOld = nStartTime = 0;
+			if(!ppsf.ReadFile(pszProcName))
+				return false;
+			pid = ppsf.pid();
+		}
+	}
+	else
+	{
+		if(!ppsf.ReadFile(pszProcName))
+		{
+			fUtimeOld = fStimeOld = 0.0;
+			nHeartbeatOld = nStartTime = 0;
+			return false;
+		}
+		pid = ppsf.pid();
+	}
+	
+	if(!nStartTime)
+		nStartTime = (clock64_t)(ppsf.starttime() * 1000000.0);
+
+	double fInt, fUse, fUtime, fStime;
+	clock64_t nHeartbeat = _GetHeartbeatUs();
+
+	fUtime = ppsf.utime();
+	fStime = ppsf.stime();
+	at.fCpuTime = fUtime + fStime;
+
+	fInt = (double)llabs(nHeartbeat - nStartTime) / 1000000.0;
+	fUse = fUtime + fStime;
+	at.fCpuAvg = fUse * 100.0 / fInt;
+
+	if(nHeartbeatOld)
+	{
+		fInt = (double)llabs(nHeartbeat - nHeartbeatOld) / 1000000.0;
+		fUse = fUtime - fUtimeOld + fStime - fStimeOld;
+		at.fCpuCur = fUse * 100.0 / fInt;
+	}
+
+	fUtimeOld = fUtime;
+	fStimeOld = fStime;
+	nHeartbeatOld = nHeartbeat;
+	return true;
+}
+
+static bool _GetAppMem(pid_t &pid, const char *pszProcName, GFA_APPCTRL_APPMEM &am)
+{
+	if(!pid && pszProcName && *pszProcName)
+	{
+		std::string cmd = CProcFile::FormatString("pidof -s %s", pszProcName);
+		FILE *pf = popen(cmd.c_str(), "r");
+		if(pf)
+		{
+			char szPid[16];
+			fgets(szPid, sizeof(szPid), pf);
+			std::string sPid(szPid);
+			pid = CProcFile::StrToIntegral(_rtrim(sPid, " \r\n\t\v"));
+			pclose(pf);
+		}
+	}
+
+	if(pid)
+	{
+	    CProcMem pm(pid);
+
+		if(pm.Update())
+		{
+			const VM_VALUE &rVmPeak = pm.VmPeak();
+		    am.vmPeak = rVmPeak.valid ? rVmPeak.cb : 0;
+
+			const VM_VALUE &rVmSize = pm.VmSize();
+		    am.vmSize = rVmSize.valid ? rVmSize.cb : 0;
+
+			const VM_VALUE &rVmHWM = pm.VmHWM();
+		    am.vmHWM = rVmHWM.valid ? rVmHWM.cb : 0;
+
+			const VM_VALUE &rVmRSS = pm.VmRSS();
+		    am.vmRSS = rVmRSS.valid ? rVmRSS.cb : 0;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static size_t _GetTableFileSize(const char *pszDataDir, const char *pszDbsName, const char *pszTableName, const char *pszExt)
+{
+	std::string sdd(pszDataDir);
+	struct stat s;
+	char szPath[PATH_MAX];
+	memset(&s, 0, sizeof(s));
+	sprintf(szPath, "%s/%s/%s.%s", _rtrim(sdd, "/").c_str(), pszDbsName, pszTableName, pszExt);
+	if(!stat(szPath, &s))
+		return (size_t)s.st_size;
+	return 0;
+}
+
+static void _ZeroSDB(GFA_SYSINFO_DATABASE &sdb, bool bInit, bool bRunning)
+{
+	if(!bInit)
+	{
+		memset(&sdb, 0, sizeof(sdb));
+		return;
+	}
+
+	if(!bRunning)
+	{
+		memset(&sdb.svr, 0, sizeof(sdb.svr));
+		memset(&sdb.res, 0, sizeof(sdb.res));
+		memset(&sdb.dbs, 0, sizeof(sdb.dbs));
+		sdb.nNumDatabases = 0;
+	}
+}
+
+static time_t _GetTableCreationDateTime(CMySqlDB &db, const char *pshSchema, const char *pszTable)
+{
+	char sql[1024];
+	sprintf(sql, "SELECT UNIX_TIMESTAMP(`CREATE_TIME`) as `ct` from `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = '%s' AND `TABLE_NAME` = '%s';", pshSchema, pszTable);
+	CMySqlResult res = db.Query(sql);
+	if(!res.error())
+	{
+		CMySqlRow row;
+		if(res.FetchRow(row))
+		{
+			time_t t = (uint64_t)row["ct"];
+			return t;
+		}
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _GetDbInfo(const std::string &sDbUser, const std::string &sDbPass, GFA_SYSINFO_DATABASE &sdb)
+{
+	static bool bSvrInit = false;
+	static cy_time_t nTsLastQ = 0, nTsLastS = 0, nTsLastL = 0;
+	cy_time_t nTsCur = CCycleTimer::GetMilliTick();
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// quick update section
+	
+	if(nTsLastQ && ((nTsLastQ + _QUICK_UPDATE_INTERVAL_MS) > nTsCur))
+		return true;
+	nTsLastQ = nTsCur;
+	sdb.bIsInit = true;
+
+	/////////////////////////////////////////////////////////////////////////
+	// cpu usage
+
+    if(!_GetAppTimes(sdb.svr.pid, _MYSQL_PROCESS_NAME, sdb.res.at))
+	{
+		_ZeroSDB(sdb, true, false);
+		bSvrInit = false;
+		return false;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// memory usage
+
+    if(!_GetAppMem(sdb.svr.pid, _MYSQL_PROCESS_NAME, sdb.res.am))
+	{
+		_ZeroSDB(sdb, true, false);
+		bSvrInit = false;
+		return false;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// connect
+
+	CMySqlDB db;
+
+	if(!db.Connect(_MYSQL_HOST, sDbUser.c_str(), sDbPass.c_str(), NULL))
+	{
+		_ZeroSDB(sdb, true, false);
+		bSvrInit = false;
+		nTsLastS = nTsLastL = 0;
+		return false;
+	}
+
+    sdb.svr.bRunning = true;
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// slow update section
+	
+	if(nTsLastS && ((nTsLastS + _SLOW_UPDATE_INTERVAL_MS) > nTsCur))
+		return true;
+	nTsLastS = nTsCur;
+
+	/////////////////////////////////////////////////////////////////////////
+	//
+
+	if(!bSvrInit)
+	{
+		CMySqlVar vPidFile = _QuerySingleServerVariable(db, "pid_file");
+
+		if(!vPidFile.IsValid())
+		{
+			_ZeroSDB(sdb, true, false);
+			nTsLastS = nTsLastL = 0;
+			return false;
+		}
+
+        sdb.svr.pid = _ReadPid(vPidFile.StrVal());
+
+		CMySqlVar vSvrVersion = _QuerySingleServerVariable(db, "version");
+
+		if(!vSvrVersion.IsValid())
+		{
+			_ZeroSDB(sdb, true, false);
+			nTsLastS = nTsLastL = 0;
+			return false;
+		}
+
+        vSvrVersion.CopyStrVal(sdb.svr.szVersion, GFA_MYSQL_MAX_SVR_VERSION_LENGTH - 1);
+
+		CMySqlVar vDataDir = _QuerySingleServerVariable(db, "datadir");
+
+		if(!vDataDir.IsValid())
+		{
+			_ZeroSDB(sdb, true, false);
+			nTsLastS = nTsLastL = 0;
+			return false;
+		}
+
+        vDataDir.CopyStrVal(sdb.svr.szDataDir, GFA_MYSQL_MAX_DATADIR_LENGTH - 1);
+
+		/////////////////////////////////////////////////////////////////////
+		// InnoDB table space
+
+		CMySqlVar vInnoDbFpT = _QuerySingleServerVariable(db, "innodb_file_per_table");
+
+		if(!vInnoDbFpT.IsValid())
+		{
+			_ZeroSDB(sdb, true, false);
+			nTsLastS = nTsLastL = 0;
+			return false;
+		}
+
+        sdb.svr.bInnoDbFilePerTable = !vInnoDbFpT.StrValCmp("ON");
+		bSvrInit = true;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// Uptime
+	
+	CMySqlVar vUptime = _QuerySingleGlobalStatusValue(db, "Uptime");
+
+	if(!vUptime.IsValid())
+	{
+		_ZeroSDB(sdb, true, false);
+		nTsLastS = nTsLastL = 0;
+		return false;
+	}
+
+    sdb.svr.nUptimeSec = CProcFile::StrToIntegral(vUptime.StrVal());
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// lazy update section
+	
+	if(nTsLastL && ((nTsLastL + _LAZY_UPDATE_INTERVAL_MS) > nTsCur))
+		return true;
+	nTsLastL = nTsCur;
+
+	sdb.svr.nDiscUsageTotal = _GetDirectoryDiscUsage(sdb.svr.szDataDir);
+
+	/////////////////////////////////////////////////////////////////////////
+	// list databases
+
+	CMySqlResult resDb = db.Query("SHOW DATABASES;");
+	sdb.nNumDatabases = 0;
+
+	if(resDb.error())
+	{
+		_ZeroSDB(sdb, true, false);
+		nTsLastS = nTsLastL = 0;
+		return false;
+	}
+
+	CMySqlRow rowDb;
+	char sql[256];
+
+	while(resDb.FetchRow(rowDb))
+	{
+		if(sdb.nNumDatabases >= GFA_MYSQL_MAX_DATABASES)
+			break;
+
+		const CMySqlVar &var = rowDb["Database"];
+
+		if(var.IsValid() && !var.IsNull())
+		{
+			if(!_IsIgnoredDb(var.StrVal()))
+			{
+				GFA_MYSQL_SCHEMA &curDb = sdb.dbs[sdb.nNumDatabases++];
+				var.CopyStrVal(curDb.szName, GFA_MYSQL_MAX_DB_NAME_LENGTH - 1);
+				sprintf(sql, "SHOW TABLE STATUS IN `%s` WHERE `Comment` != 'VIEW';", var.StrVal());
+
+				CMySqlRow rowTab;
+				CMySqlResult resTab = db.Query(sql);
+				curDb.nNumTables = 0;
+				curDb.nSizeTotal = 0;
+
+				if(!resTab.error())
+				{
+					while(resTab.FetchRow(rowTab))
+					{
+						if(curDb.nNumTables >= GFA_MYSQL_MAX_TABLES_PER_DATABASE)
+							break;
+
+						GFA_MYSQL_TABLE &curTab		= curDb.tables[curDb.nNumTables++];
+						const CMySqlVar &vName		= rowTab["Name"];
+						const CMySqlVar &vEngine	= rowTab["Engine"];
+						const CMySqlVar &vVersion	= rowTab["Version"];
+						const CMySqlVar &vRowFormat	= rowTab["Row_format"];
+						const CMySqlVar &vCollation	= rowTab["Collation"];
+
+						if(vVersion.IsValid())
+							curTab.nVersion = vVersion;
+						if(vName.IsValid())
+							vName.CopyStrVal(curTab.szName, GFA_MYSQL_MAX_TABLE_NAME_LENGTH - 1);
+						if(vEngine.IsValid())
+							vEngine.CopyStrVal(curTab.szEngine, GFA_MYSQL_MAX_ENGINE_NAME_LENGTH - 1);
+						if(vRowFormat.IsValid())
+							vRowFormat.CopyStrVal(curTab.szRowFormat, GFA_MYSQL_MAX_ROW_FORMAT_LENGTH - 1);
+						if(vCollation.IsValid())
+							vCollation.CopyStrVal(curTab.szCollation, GFA_MYSQL_MAX_COLLATION_LENGTH - 1);
+
+                        if(!vEngine.StrValCmp("InnoDB") && sdb.svr.bInnoDbFilePerTable)
+						{
+							// InnoDB sizes are rough estimates and not reliable, so we simply use the size of the table files, which
+							// of course works only in a file-per-table tablespace!
+							curTab.nSizeTotal = 0;
+                            curTab.nSizeTotal += _GetTableFileSize(sdb.svr.szDataDir, curDb.szName, curTab.szName, "ibd");
+                            curTab.nSizeTotal += _GetTableFileSize(sdb.svr.szDataDir, curDb.szName, curTab.szName, "frm");
+                            curDb.nSizeTotal += curTab.nSizeTotal;
+						}
+						
+						curTab.nCreateTime = _GetTableCreationDateTime(db, curDb.szName, curTab.szName);
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+CMySqlInfo::CMySqlInfo(void) : m_bPaused(false)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void* CMySqlInfo::ThreadRoutine(void *pParam)
+{
+	LPEXEC_PARAMS pep = (LPEXEC_PARAMS)pParam;
+
+	if(pep)
+	{
+		bool bRun = true;
+		int nRet, nSig;
+		GFA_SYSINFO_DATABASE sdb;
+
+		while(bRun)
+		{
+			if((nRet = WaitSignalTimeout(_QUICK_UPDATE_INTERVAL_MS * 1000, &nSig)) == ETIMEDOUT)
+			{
+//				TRACE("%s wait timeout.\n", __FUNCTION__);
+				if(!m_bPaused)
+				{
+					::_GetDbInfo(pep->sUser, pep->sPass, sdb);
+					::GfaIpcAppCtrlUpdateDbInfo(pep->hAC, sdb);
+				}
+			}
+			else if(!nRet) // signal received
+			{
+				TRACE("%s signal %d received.\n", __FUNCTION__, nSig);
+
+				switch(nSig)
+				{
+				case S_Update:
+					::_GetDbInfo(pep->sUser, pep->sPass, sdb);
+					::GfaIpcAppCtrlUpdateDbInfo(pep->hAC, sdb);
+					break;
+				case S_UpdateAll:
+					::_GetDbInfo(pep->sUser, pep->sPass, sdb);
+					::GfaIpcAppCtrlUpdateDbInfo(pep->hAC, sdb);
+					break;
+				case S_Pause:
+					m_bPaused = true;
+					break;
+				case S_Resume:
+					m_bPaused = false;
+					break;
+				case S_Terminate:
+					memset(&sdb, 0, sizeof(sdb));
+					::GfaIpcAppCtrlUpdateDbInfo(pep->hAC, sdb);
+					bRun = false;
+					break;
+				default:
+					break;
+				}
+			}
+			else
+			{
+//				TRACE("%s error %d.\n", __FUNCTION__, nRet);
+			}
+		}
+	}
+
+	TRACE("%s exit.\n", __FUNCTION__);
+	return NULL;
+}

+ 47 - 0
gfasysinfo/src/mysqlinfo.h

@@ -0,0 +1,47 @@
+// mysqlinfo.h :
+//
+
+#if !defined(AGD_MYSQLINFO_H__C9901321_C314_4599_982B_EC457FAFDAA0__INCLUDED_)
+#define AGD_MYSQLINFO_H__C9901321_C314_4599_982B_EC457FAFDAA0__INCLUDED_
+
+#include <string>
+#include <gfa/gfaipc.h>
+#include <gfa/ipcpriv.h>
+#include <gfa/thread.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// mysqlinfo.h - Declarations:
+
+class CMySqlInfo : public CThread
+{
+public:
+	typedef enum
+	{
+		S_Update = 1,
+		S_UpdateAll,
+		S_Pause,
+		S_Resume,
+		S_Terminate
+	}Signals;
+
+public:
+	typedef struct _EXEC_PARAMS
+	{
+		HAPPCTRL hAC;
+		std::string sUser;
+		std::string sPass;
+	}EXEC_PARAMS, *LPEXEC_PARAMS;
+	typedef const EXEC_PARAMS *LPCEXEC_PARAMS;
+
+public:
+	CMySqlInfo(void);
+
+protected:
+	virtual void* ThreadRoutine(void *pParam);
+
+private:
+	bool m_bPaused;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_MYSQLINFO_H__C9901321_C314_4599_982B_EC457FAFDAA0__INCLUDED_)

+ 418 - 0
gfasysinfo/src/mysqlwrap.cpp

@@ -0,0 +1,418 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <utility>
+#include <math.h>
+#include "mysqlwrap.h"
+
+CMySqlDB::CMySqlDB(void) : m_fIsInit(false)
+{
+	memset(&m_mySql, 0, sizeof(m_mySql));
+	Init();
+}
+
+CMySqlDB::~CMySqlDB(void)
+{
+	Close();
+}
+
+bool CMySqlDB::Init(void) throw()
+{
+	if(!m_fIsInit)
+		m_fIsInit = !!mysql_init(&m_mySql);
+	return m_fIsInit;
+}
+
+void CMySqlDB::Close(void) throw()
+{
+	if(m_fIsInit)
+	{
+		mysql_close(&m_mySql);
+		m_fIsInit = false;
+		memset(&m_mySql, 0, sizeof(m_mySql));
+	}
+}
+
+bool CMySqlDB::Connect(	const char *host,
+						const char *user,
+						const char *pass,
+						const char *db,
+						unsigned int port,
+						const char *unix_socket,
+						unsigned long client_flag)
+{
+	if(!Init())
+		return false;
+	return !!mysql_real_connect(&m_mySql, host, user, pass, db, port, unix_socket, client_flag);
+}
+
+int CMySqlDB::SetCharSet(const char *pszCharset)
+{
+	return !mysql_set_character_set(&m_mySql, pszCharset);
+}
+
+int CMySqlDB::Options(enum mysql_option option, const void *arg)
+{
+	return !mysql_options(&m_mySql, option, arg); 
+}
+
+CMySqlResult CMySqlDB::Query(const char *sql)
+{
+	bool bErr = true;
+	MYSQL_RES *pRes = NULL;
+	
+	if(m_fIsInit)
+	{
+		if(!(bErr = !!mysql_query(&m_mySql, sql)))
+			pRes = mysql_store_result(&m_mySql);
+	}
+
+	return CMySqlResult(pRes, bErr);
+}
+
+int CMySqlDB::SelectDB(const char *db)
+{
+	return mysql_select_db(&m_mySql, db);
+}
+
+std::string CMySqlDB::EscapeStringQuote(const char *from, char quote)
+{
+	if(	m_fIsInit &&
+		from && *from)
+	{
+		size_t len = strlen(from);
+		size_t lenBuf = len * 2 + 1;
+		char *buf = (char*)alloca(lenBuf);
+#if MYSQL_VERSION_ID >= 50706
+		if(mysql_real_escape_string_quote(&m_mySql, buf, from, (unsigned long)len, quote))
+#else	//	MYSQL_VERSION_ID
+        (void)quote;
+		if(mysql_real_escape_string(&m_mySql, buf, from, (unsigned long)len))
+#endif	//	MYSQL_VERSION_ID
+			return buf;
+	}
+
+	return "";
+}
+
+std::string CMySqlDB::EscapeString(const char *from)
+{
+	if(	m_fIsInit &&
+		from && *from)
+	{
+		size_t len = strlen(from);
+		size_t lenBuf = len * 2 + 1;
+		char *buf = (char*)alloca(lenBuf);
+		if(mysql_real_escape_string(&m_mySql, buf, from, (unsigned long)len))
+			return buf;
+	}
+
+	return "";
+}
+
+std::string CMySqlDB::LastError(void)
+{
+	if(m_fIsInit)
+		return mysql_error(&m_mySql);
+	else
+		return "MySQL not initialized!";
+}
+
+unsigned int CMySqlDB::LastErrno(void)
+{
+	if(m_fIsInit)
+		return mysql_errno(&m_mySql);
+	else
+		return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+CMySqlResult::CMySqlResult(MYSQL_RES *pRes, bool err) :	m_pRes(pRes),
+														m_bError(err)
+{
+}
+
+CMySqlResult::~CMySqlResult(void)
+{
+	Free();
+}
+
+void CMySqlResult::Free(void)
+{
+	if(m_pRes)
+	{
+		mysql_free_result(m_pRes);
+		m_pRes = NULL;
+	}
+}
+
+my_ulonglong CMySqlResult::RowCount(void) const
+{
+	if(m_pRes)
+		return mysql_num_rows(m_pRes);
+	return 0;
+}
+
+unsigned int CMySqlResult::FieldCount(void) const
+{
+	if(m_pRes)
+		return mysql_num_fields(m_pRes);
+	return 0;
+}
+
+MYSQL_ROW CMySqlResult::FetchRow(void)
+{
+	if(m_pRes)
+		return mysql_fetch_row(m_pRes);
+	return NULL;
+}
+
+bool CMySqlResult::FetchRow(CMySqlRow &row) const
+{
+	if(m_pRes)
+	{
+		MYSQL_ROW pRow = mysql_fetch_row(m_pRes);
+		if(pRow)
+			return row.Create(FieldCount(), FetchFields(), pRow);
+	}
+
+	return false;
+}
+
+const MYSQL_FIELD * CMySqlResult::FetchFields(void) const
+{
+	if(m_pRes)
+		return mysql_fetch_fields(m_pRes);
+	return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+const CMySqlVar CMySqlRow::m_vNul;
+
+CMySqlRow::CMySqlRow(void) : m_bValid(false), m_nFieldCount(0)
+{
+}
+
+CMySqlRow::~CMySqlRow(void)
+{
+}
+
+void CMySqlRow::Clear(void)
+{
+	m_bValid = false;
+	m_nFieldCount = 0;
+	m_fields.clear();
+}
+
+bool CMySqlRow::Create(unsigned int nFieldCount, const MYSQL_FIELD *pFields, MYSQL_ROW pRow)
+{
+	Clear();
+	
+	if((nFieldCount > 0) && pFields && pRow)
+	{
+		for(m_nFieldCount = 0; m_nFieldCount < nFieldCount; ++m_nFieldCount)
+		{
+			std::string s(pFields[m_nFieldCount].name);
+			if(!m_fields.emplace(s, std::move(CMySqlVar(pFields[m_nFieldCount], pRow[m_nFieldCount]))).second)
+			{
+				Clear();
+				return false;
+			}
+		}
+		
+		m_bValid = true;
+	}
+	
+	return m_bValid;
+}
+
+const CMySqlVar& CMySqlRow::Value(const char *pszFieldname) const
+{
+	if(m_bValid)
+	{
+		CMySqlFieldMap::const_iterator it;
+		if(FindField(pszFieldname, it))
+			return it->second;
+	}
+
+	return m_vNul;
+}
+
+CMySqlRow::CMySqlFieldMap::const_iterator CMySqlRow::begin(void)
+{
+	return m_fields.begin();
+}
+
+CMySqlRow::CMySqlFieldMap::const_iterator CMySqlRow::end(void)
+{
+	return m_fields.end();
+}
+
+bool CMySqlRow::FindField(const char *pszFieldname, CMySqlFieldMap::const_iterator &it) const
+{
+	if(m_bValid)
+	{
+		it = m_fields.find(pszFieldname);
+		return (it != m_fields.end());
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+CMySqlVar::CMySqlVar(void)
+{
+	Clear();
+}
+
+CMySqlVar::CMySqlVar(const MYSQL_FIELD &rField, const char *pszVal)
+{
+	FromField(rField, pszVal);
+}
+
+CMySqlVar::CMySqlVar(CMySqlVar &&o) : 	m_bValid(std::move(o.m_bValid)), m_bInteger(std::move(o.m_bInteger)), m_bReal(std::move(o.m_bReal)), m_bUnsigned(std::move(o.m_bUnsigned)), m_bString(std::move(o.m_bString)),
+										m_strVal(std::move(o.m_strVal)), m_strFieldname(std::move(o.m_strFieldname)), m_sqlFt(std::move(o.m_sqlFt)), m_numVal(std::move(o.m_numVal))
+{
+	if(this != &o)
+		o.Clear();
+}
+
+CMySqlVar::CMySqlVar(const CMySqlVar &o) : 	m_bValid(o.m_bValid), m_bInteger(o.m_bInteger), m_bReal(o.m_bReal), m_bUnsigned(o.m_bUnsigned), m_bString(o.m_bString),
+											m_strVal(o.m_strVal), m_strFieldname(o.m_strFieldname), m_sqlFt(o.m_sqlFt), m_numVal(o.m_numVal)
+{
+}
+
+CMySqlVar::~CMySqlVar(void)
+{
+}
+
+void CMySqlVar::Clear(void)
+{
+	m_bValid		= false;
+	m_bUnsigned		= false;
+	m_bInteger		= false;
+	m_bReal			= false;
+	m_bString		= false;
+	m_strFieldname.clear();
+	m_strVal.clear();
+	m_sqlFt			= MYSQL_TYPE_NULL;
+	m_numVal.uVal	= 0;
+}
+
+bool CMySqlVar::FromField(const MYSQL_FIELD &rField, const char *pszVal)
+{
+	Clear();
+
+	if(pszVal)
+	{
+		char *pszEndPtr;
+		bool bUnsigned = !!(rField.flags & UNSIGNED_FLAG);
+		m_strFieldname = rField.name;
+
+		switch(rField.type)
+		{
+		case MYSQL_TYPE_BIT:
+		case MYSQL_TYPE_TINY:		//  8 Bit =>  8 Bit
+		case MYSQL_TYPE_SHORT:		// 16 Bit => 16 Bit
+		case MYSQL_TYPE_INT24:		// 24 Bit => 32 Bit
+		case MYSQL_TYPE_LONG:		// 32 Bit => 32 Bit
+		case MYSQL_TYPE_LONGLONG:	// 64 Bit => 64 Bit
+			if(bUnsigned)
+			{
+				int64_t val = strtoll(pszVal, &pszEndPtr, 10);
+				if(!*pszVal || ((val == LLONG_MIN || val == LLONG_MAX) && errno == ERANGE) || *pszEndPtr)
+					m_bValid = false;
+				else
+				{
+					m_sqlFt = rField.type;
+					m_strVal = pszVal;
+					m_numVal.iVal = val;
+					m_bUnsigned = true;
+					m_bInteger = true;
+					m_bValid = true;
+				}
+			}
+			else
+			{
+				uint64_t val = strtoull(pszVal, &pszEndPtr, 10);
+				if(!*pszVal || (val == ULLONG_MAX && errno == ERANGE) || *pszEndPtr)
+					m_bValid = false;
+				else
+				{
+					m_sqlFt = rField.type;
+					m_strVal = pszVal;
+					m_numVal.uVal = val;
+					m_bInteger = true;
+					m_bValid = true;
+				}
+			}
+			break;
+
+		case MYSQL_TYPE_FLOAT:
+		case MYSQL_TYPE_DOUBLE:
+		case MYSQL_TYPE_DECIMAL:
+		case MYSQL_TYPE_NEWDECIMAL:
+			{
+				double val = strtod(pszVal, &pszEndPtr);
+				if(!*pszVal || ((val == HUGE_VAL || val == -HUGE_VAL) && errno == ERANGE) || *pszEndPtr)
+					m_bValid = false;
+				else
+				{
+					m_sqlFt = rField.type;
+					m_strVal = pszVal;
+					m_numVal.fVal = val;
+					m_bReal = true;
+					m_bValid = true;
+				}
+			}
+			break;
+
+		case MYSQL_TYPE_VARCHAR:
+		case MYSQL_TYPE_VAR_STRING:
+		case MYSQL_TYPE_STRING:
+			m_sqlFt = rField.type;
+			m_strVal = pszVal;
+			m_bString = true;
+			m_bValid = true;
+			break;
+
+		case MYSQL_TYPE_DATE:
+		case MYSQL_TYPE_TIME:
+		case MYSQL_TYPE_DATETIME:
+			m_sqlFt = rField.type;
+			m_strVal = pszVal;
+			m_bValid = true;
+			break;
+		case MYSQL_TYPE_TIMESTAMP:
+		case MYSQL_TYPE_YEAR:
+		case MYSQL_TYPE_NEWDATE:
+		case MYSQL_TYPE_ENUM:
+		case MYSQL_TYPE_SET:
+		case MYSQL_TYPE_TINY_BLOB:
+		case MYSQL_TYPE_MEDIUM_BLOB:
+		case MYSQL_TYPE_LONG_BLOB:
+		case MYSQL_TYPE_BLOB:
+		case MYSQL_TYPE_GEOMETRY:
+			break;
+
+		case MYSQL_TYPE_NULL:
+			m_sqlFt = rField.type;
+			m_strVal = "";
+			m_bValid = true;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return m_bValid;
+}

+ 192 - 0
gfasysinfo/src/mysqlwrap.h

@@ -0,0 +1,192 @@
+// mysqlwrap.h :
+//
+
+#if !defined(AGD_MYSQLWRAP_H__0FDF8710_B26A_4631_A025_705731DF316E__INCLUDED_)
+#define AGD_MYSQLWRAP_H__0FDF8710_B26A_4631_A025_705731DF316E__INCLUDED_
+
+#include <stdint.h>
+#include <mysql/mysql.h>
+#include <string>
+#include <unordered_map>
+#include <functional>
+//#include "debug.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// mysqlwrap.h - Declarations:
+
+class CMySqlVar
+{
+public:
+	CMySqlVar(void);
+	CMySqlVar(CMySqlVar &&o);
+	CMySqlVar(const CMySqlVar &o);
+	CMySqlVar(const MYSQL_FIELD &rField, const char *pszVal);
+	virtual ~CMySqlVar(void);
+
+	void Clear(void);
+
+	bool FromField(const MYSQL_FIELD &rField, const char *pszVal);
+
+	inline bool IsValid(void) const {
+		return m_bValid;}
+
+	inline bool IsNumeric(void) const {
+		return m_bValid && (m_bInteger || m_bReal);}
+
+	inline bool IsReal(void) const {
+		return m_bValid && m_bReal;}
+
+	inline bool IsUnsigned(void) const {
+		return m_bValid && m_bUnsigned;}
+
+	inline bool IsNull(void) const {
+		return m_sqlFt == MYSQL_TYPE_NULL;}
+		
+	inline enum enum_field_types GetSqlFieldType(void) const {
+		return m_sqlFt;}
+
+	inline const char* StrVal(void) const {
+		return m_strVal.c_str();}
+
+	inline int StrValCmp(const char *pszCmp) const {
+		return m_strVal.compare(pszCmp);}
+
+	inline size_t CopyStrVal(char *s, size_t len, size_t pos = 0) const {
+		return m_strVal.copy(s, len, pos);}
+
+	inline const char* Fieldname(void) const {
+		return m_strFieldname.c_str();}
+
+	inline int FieldnameCmp(const char *pszName) const {
+		return m_strFieldname.compare(pszName);}
+
+	operator int (void) const {
+		return m_bInteger ? (int)m_numVal.iVal : 0;}
+
+	operator int64_t (void) const {
+		return m_bInteger ? m_numVal.iVal : 0;}
+
+	operator uint64_t (void) const {
+		return m_bInteger ? m_numVal.uVal : 0;}
+
+#ifndef _TARGET_BUILD
+	operator long long (void) const {
+		return m_bInteger ? (long long)m_numVal.iVal : 0;}
+
+	operator unsigned long long (void) const {
+		return m_bInteger ? (unsigned long long)m_numVal.uVal : 0;}
+#endif  //  _TARGET_BUILD
+
+	operator bool (void) const {
+		return m_bInteger ? !!m_numVal.uVal : false;}
+
+	operator double (void) const {
+		return m_bReal ? m_numVal.fVal : 0.0;}
+
+private:
+	bool m_bValid;
+	bool m_bInteger;
+	bool m_bReal;
+	bool m_bUnsigned;
+	bool m_bString;
+	std::string m_strVal;
+	std::string m_strFieldname;
+	enum enum_field_types m_sqlFt;
+	union
+	{
+		int64_t		iVal;
+		uint64_t	uVal;
+		double		fVal;
+	}m_numVal;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CMySqlRow
+{
+public:
+	typedef std::unordered_map<std::string, CMySqlVar> CMySqlFieldMap;
+
+public:
+	CMySqlRow(void);
+	~CMySqlRow(void);
+
+public:
+	void Clear(void);
+	bool Create(unsigned int nFieldCount, const MYSQL_FIELD *pFields, MYSQL_ROW row);
+	const CMySqlVar& Value(const char *pszFieldname) const;
+
+	inline const CMySqlVar& operator [](const char *pszFieldname) const {
+		return Value(pszFieldname);}
+
+	inline bool IsValid(void) const {
+		return m_bValid;}
+		
+	CMySqlFieldMap::const_iterator begin(void);
+	CMySqlFieldMap::const_iterator end(void);
+
+private:
+	bool FindField(const char *pszFieldname, CMySqlFieldMap::const_iterator &it) const;
+
+private:
+	bool m_bValid;
+	unsigned int m_nFieldCount;
+	CMySqlFieldMap m_fields;
+	static const CMySqlVar m_vNul;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CMySqlResult
+{
+public:
+	CMySqlResult(MYSQL_RES *pRes, bool err);
+	virtual ~CMySqlResult(void);
+
+public:
+	void Free(void);
+
+	my_ulonglong RowCount(void) const;
+	unsigned int FieldCount(void) const;
+	MYSQL_ROW FetchRow(void);
+	bool FetchRow(CMySqlRow &row) const;
+	const MYSQL_FIELD * FetchFields(void) const;
+
+	inline bool error(void) const {
+		return m_bError;}
+
+private:
+	MYSQL_RES *m_pRes;
+	bool m_bError;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CMySqlDB
+{
+public:
+	CMySqlDB(void);
+	virtual ~CMySqlDB(void);
+
+public:
+	bool Init(void) throw();
+	void Close(void) throw();
+
+	bool Connect(const char *host, const char *user, const char *pass, const char *db, unsigned int port = 0, const char *unix_socket = NULL, unsigned long client_flag = 0);
+	CMySqlResult Query(const char *sql);
+	int SelectDB(const char *db);
+	int SetCharSet(const char *pszCharset);
+	int Options(enum mysql_option option, const void *arg);
+
+	std::string EscapeStringQuote(const char *from, char quote);
+	std::string EscapeString(const char *from);
+	std::string LastError(void);
+	unsigned int LastErrno(void);
+
+private:
+	MYSQL m_mySql;
+	bool m_fIsInit;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_MYSQLWRAP_H__0FDF8710_B26A_4631_A025_705731DF316E__INCLUDED_)

+ 147 - 0
gfasysinfo/src/procfile.cpp

@@ -0,0 +1,147 @@
+#include <limits.h>
+#include <stdarg.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <regex>
+#include "procfile.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+const std::string CProcFile::m_sEmpty;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+CProcFile::CProcFile(void)
+{
+	m_nClkPerSec = sysconf(_SC_CLK_TCK);
+}
+
+CProcFile::~CProcFile(void)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+std::string CProcFile::FormatString(const char *fmt, ...)
+{
+	int n;
+	std::string s;
+	char *p = NULL;
+	va_list ap;
+	va_start(ap, fmt);
+	n = ::vasprintf(&p, fmt, ap);
+	va_end(ap);
+	if(n >= 0)
+	{
+		s = p;
+		free(p);
+	}
+	return s;
+}
+
+int CProcFile::SplitLine(const std::string &str, std::vector<std::string> &vec, const char *rxe)
+{
+	if(str.empty())
+		return 0;
+
+	int nElems = 0;
+	std::regex reg(rxe);
+	const std::sregex_token_iterator end;
+
+	for(std::sregex_token_iterator iter(str.begin(), str.end(), reg); iter != end; ++iter)
+	{
+		vec.push_back(*iter);
+		nElems++;
+	}
+
+	return nElems;
+}
+
+long long CProcFile::StrToIntegral(const std::string &str, long long nDefault, int nBase)
+{
+	if(str.empty())
+		return nDefault;
+
+	char *pszEnd = NULL;
+	long long val = strtoll(str.c_str(), &pszEnd, nBase);
+	
+	if((((val == LLONG_MIN) || (val == LLONG_MAX)) && errno == ERANGE) || (pszEnd && *pszEnd))
+		val = nDefault;
+
+	return val;
+}
+
+double CProcFile::StrToReal(const std::string &str, double fDefault)
+{
+	if(str.empty())
+		return fDefault;
+
+	char *pszEnd = NULL;
+	double val = strtod(str.c_str(), &pszEnd);
+	
+	if((((val == HUGE_VAL) || (val == -HUGE_VAL)) && errno == ERANGE) || (pszEnd && *pszEnd))
+		val = fDefault;
+
+	return val;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+bool CProcFile::ReadFile(const char *pszFilePath)
+{
+	m_vMap.clear();
+
+	std::string sLine;
+	std::ifstream fs(pszFilePath);
+	bool bRet = fs.good();
+
+	while(fs.good())
+	{
+		std::vector<std::string> v;
+		std::getline(fs, sLine);
+
+		if(SplitLine(sLine, v) > 1)
+		{
+			std::string s(v[0]);
+//			v.erase(v.begin());
+			m_vMap[s] = v;
+		}
+	}
+
+	return bRet;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+
+static const std::string & _rtrim(std::string &s)
+{
+    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
+	return s;
+}
+
+bool CProcPidStatFile::ReadFile(const char *pszProcName)
+{
+	if(!pszProcName || !*pszProcName)
+		return false;
+	std::string cmd = FormatString("pidof -s %s", pszProcName);
+	FILE *pf = popen(cmd.c_str(), "r");
+	if(pf)
+	{
+		char szPid[16];
+		fgets(szPid, sizeof(szPid), pf);
+		std::string sPid(szPid);
+		pid_t pid = StrToIntegral(_rtrim(sPid));
+		pclose(pf);
+		return ReadFile(pid);
+	}
+	return false;
+}

+ 121 - 0
gfasysinfo/src/procfile.h

@@ -0,0 +1,121 @@
+// procfile.h :
+//
+
+#if !defined(AGD_PROCFILE_H__2D6A6629_BE8B_45D1_949D_3A434336E4DF__INCLUDED_)
+#define AGD_PROCFILE_H__2D6A6629_BE8B_45D1_949D_3A434336E4DF__INCLUDED_
+
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include <map>
+
+#ifdef __cplusplus
+
+// https://man7.org/linux/man-pages/man5/proc.5.html
+
+/////////////////////////////////////////////////////////////////////////////
+// procfile.h - Declarations:
+
+#define _DEFAULT_REGEX_EXPRESSION			"[^\\s]+"
+
+class CProcFile
+{
+public:
+	typedef std::map<std::string, std::vector<std::string>> KeyMultiValueMap;
+
+public:
+	CProcFile(void);
+	virtual ~CProcFile(void);
+
+	virtual bool ReadFile(const char *pszFilePath);
+	
+	const std::string & GetValue(const std::string &sKey, int index) {
+		if(index >= 0)
+		{
+			const std::vector<std::string> &m = m_vMap[sKey];
+			if(m.size() > (size_t)index)
+				return m[index];
+		}
+		return m_sEmpty;
+	}
+
+	const std::vector<std::string> & operator[] (const std::string &sKey) {
+		return m_vMap[sKey];
+	}
+
+public:
+	static std::string FormatString(const char *fmt, ...);
+	static int SplitLine(const std::string &str, std::vector<std::string> &vec, const char *rxe = _DEFAULT_REGEX_EXPRESSION);
+	static long long StrToIntegral(const std::string &str, long long nDefault = 0, int nBase = 10);
+	static double StrToReal(const std::string &str, double fDefault = 0.0);
+
+protected:
+	long m_nClkPerSec;
+
+private:
+	static const std::string m_sEmpty;
+	KeyMultiValueMap m_vMap;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CProcStatFile : public CProcFile
+{
+public:
+	virtual bool ReadFile(void) {
+		return CProcFile::ReadFile("/proc/stat");}
+
+	double utime(void) {
+		double val = StrToReal(GetValue("cpu", 1));
+		return (val / (double)m_nClkPerSec);
+	}
+
+	double stime(void) {
+		double val = StrToReal(GetValue("cpu", 3));
+		return (val / (double)m_nClkPerSec);
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CProcPidStatFile : public CProcStatFile
+{
+public:
+	virtual bool ReadFile(pid_t pid) {
+		m_sPid = FormatString("%d", pid);
+		std::string s = FormatString("/proc/%d/stat", pid);
+		return CProcFile::ReadFile(s.c_str());
+	}
+
+	virtual bool ReadFile(const char *pszProcName);
+
+	const std::string & operator[] (int index) {
+		return GetValue(m_sPid, index);
+	}
+	
+	pid_t pid(void) {
+		return (pid_t)StrToIntegral((*this)[0]);
+	}
+	
+	double utime(void) {
+		double val = StrToReal((*this)[13]);
+		return (val / (double)m_nClkPerSec);
+	}
+	
+	double stime(void) {
+		double val = StrToReal((*this)[14]);
+		return (val / (double)m_nClkPerSec);
+	}
+
+	unsigned long long starttime(void) {
+		double val = StrToReal((*this)[21]);
+		return (val / (double)m_nClkPerSec);
+	}
+
+private:
+	std::string m_sPid;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_PROCFILE_H__2D6A6629_BE8B_45D1_949D_3A434336E4DF__INCLUDED_)

+ 777 - 0
gfasysinfo/src/stgdevinfo.cpp

@@ -0,0 +1,777 @@
+#include <stdio.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <ext/stdio_filebuf.h>
+#include <iostream>
+#include <fstream>
+#include <signal.h>
+#include <sys/statvfs.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <libudev.h>
+#include <poll.h>
+#include <errno.h>
+#include <getopt.h>
+#include <gfa/gfasitarautils.h>
+#include "stgdevinfo.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef _DEBUG
+#define TRACE(...)							fprintf(stdout, __VA_ARGS__), fflush(stdout)
+#else	//	_DEBUG
+#define TRACE(...)
+#endif	//	_DEBUG
+
+#define UNUSED(v)							(void)v
+#define _countof(a)							(sizeof(a) / sizeof(*a))
+#define _STR_EQUALS(s, t)					(!!s && !strcmp(s, t))
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _MOUNTS_FILE						"/proc/mounts"
+#define _INTERNAL_EMMC_DISK					"mmcblk1"
+#define _INTERNAL_EMMC_PART1				"mmcblk1p1"
+#define _INTERNAL_EMMC_PART2				"mmcblk1p2"
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef std::map<std::string, std::string>	MountMap;
+
+/////////////////////////////////////////////////////////////////////////////
+
+static long long	_NumberFromString(const char *pszString, int base = 10, bool *pbErr = NULL);
+static const char*	_ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, char *pszValue, size_t nCChValue, bool bTruncate = false);
+static long long	_ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, int base = 10, bool *pbErr = NULL);
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static int _LookupPartition(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode)
+{
+	int nIndex = -1;
+
+	if(pszDevNode && *pszDevNode)
+	{
+		for(size_t i = 0; i < _countof(sdm.parts); ++i)
+		{
+			if(sdm.parts[i].valid && !strcmp(sdm.parts[i].szDevNode, pszDevNode))
+			{
+				nIndex = i;	// found partition
+				break;
+			}
+		}
+	}
+
+	return nIndex;
+}
+
+static int _AddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_PARTITION &part, bool &bChange)
+{
+	int nIndex = -1;
+	bChange = false;
+
+	if((nIndex = _LookupPartition(sdm, part.szDevNode)) >= 0)
+		return nIndex; // partition already exists
+
+	for(size_t i = 0; i < _countof(sdm.parts); ++i)
+	{
+		if(!sdm.parts[i].valid)
+		{
+			memcpy(&sdm.parts[i], &part, sizeof(GFA_SYSINFO_PARTITION));
+			sdm.parts[i].valid = true;
+			bChange = true;
+			nIndex = i;
+			break;
+		}
+	}
+
+	return nIndex;
+}
+
+static int _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, bool &bChange)
+{
+	bChange = false;
+
+	if(nIndex >= 0 && nIndex < (int)_countof(sdm.parts))
+	{
+		bChange = sdm.parts[nIndex].valid;
+		sdm.parts[nIndex].valid = false;
+	}
+
+	return nIndex;
+}
+
+static bool _PartitionSetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx, int nDiskIdx, bool &bChange)
+{
+	bChange = false;
+
+	if(	(nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks)))
+	{
+		if(sdm.parts[nPartIdx].nDiskIdx != nDiskIdx)
+		{
+			sdm.parts[nPartIdx].nDiskIdx = nDiskIdx;
+			bChange = true;
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+static int _PartitionGetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx)
+{
+	if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)))
+		return sdm.parts[nPartIdx].nDiskIdx;
+	return -1;
+}
+
+static void _ClearMapChanges(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	sdm.nPartChangeMask = 0;
+}
+
+static bool _DeviceMapChanged(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	return !!sdm.nPartChangeMask;
+}
+
+static unsigned int _SetPartitionChange(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx)
+{
+	if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)))
+	{
+		unsigned int nChanged = (0x01 << nPartIdx);
+		sdm.nPartChangeMask |= nChanged;
+	}
+
+	return sdm.nPartChangeMask;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int _LookupDisk(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode)
+{
+	int nIndex = -1;
+
+	if(pszDevNode && *pszDevNode)
+	{
+		for(size_t i = 0; i < _countof(sdm.disks); ++i)
+		{
+			if(sdm.disks[i].valid && !strcmp(sdm.disks[i].szDevNode, pszDevNode))
+			{
+				nIndex = i;	// found partition
+				break;
+			}
+		}
+	}
+
+	return nIndex;
+}
+
+static int _AddDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_DISK &disk, bool &bChange)
+{
+	int nIndex = -1;
+	bChange = false;
+
+	if((nIndex = _LookupDisk(sdm, disk.szDevNode)) >= 0)
+		return nIndex; // partition already exists
+
+	for(size_t i = 0; i < _countof(sdm.disks); ++i)
+	{
+		if(!sdm.disks[i].valid)
+		{
+			memcpy(&sdm.disks[i], &disk, sizeof(GFA_SYSINFO_DISK));
+			sdm.disks[i].valid = true;
+			bChange = true;
+			nIndex = i;
+			break;
+		}
+	}
+
+	return nIndex;
+}
+
+static int _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, bool &bChange)
+{
+	bChange = false;
+
+	if(nIndex >= 0 && nIndex < (int)_countof(sdm.disks))
+	{
+		bChange = sdm.disks[nIndex].valid;
+		sdm.disks[nIndex].valid = false;
+	}
+
+	return nIndex;
+}
+
+static unsigned int _DiskAddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nDiskIdx, int nPartIdx, bool &bChange)
+{
+	bChange = false;
+
+	if(	(nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks)))
+	{
+		GFA_SYSINFO_DISK &disk = sdm.disks[nDiskIdx];
+
+		for(unsigned int i = 0; i < disk.nPartCount; ++i)
+		{
+			if(disk.aPartIdx[i] == nPartIdx)
+				return disk.nPartCount;
+		}
+
+		if(disk.nPartCount < _countof(disk.aPartIdx))
+		{
+			disk.aPartIdx[disk.nPartCount++] = nPartIdx;
+			bChange = true;
+			return disk.nPartCount;
+		}
+	}
+
+	return 0;
+}
+
+static unsigned int _DiskRemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nDiskIdx, int nPartIdx, bool &bChange)
+{
+	bChange = false;
+
+	if(	(nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks)))
+	{
+		unsigned int i, j;
+		GFA_SYSINFO_DISK &disk = sdm.disks[nDiskIdx];
+
+		for(i = 0; i < disk.nPartCount; ++i)
+		{
+			if(disk.aPartIdx[i] == nPartIdx)
+				break;
+		}
+
+		if(i < disk.nPartCount)
+		{
+			for(j = i + 1; j < disk.nPartCount; ++i, ++j)
+			{
+				disk.aPartIdx[i] = disk.aPartIdx[j];
+			}
+			bChange = true;
+			return --disk.nPartCount;
+		}
+
+		return disk.nPartCount;
+	}
+
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _LookupMountPoint(MountMap &mm, const char *pszNode, char *pszMntPoint, size_t nCChMntPoint)
+{
+	MountMap::const_iterator it = mm.find(pszNode);
+	if(it == mm.end())
+		return false;
+	const std::string &s = it->second;
+
+	if(s.length() < nCChMntPoint)
+	{
+		strcpy(pszMntPoint, s.c_str());
+		return true;
+	}
+
+	return false;
+}
+
+static bool _UpdatePartitionFsInfo(MountMap &mm, GFA_SYSINFO_PARTITION &part)
+{
+	if(_LookupMountPoint(mm, part.szDevNode, part.szMntPoint, sizeof(part.szMntPoint)))
+	{
+		struct statvfs stvs;
+
+		if(!statvfs(part.szMntPoint, &stvs))
+		{
+			part.nKiBSize = stvs.f_bsize * stvs.f_blocks	/ 1024;
+			part.nKiBFree = stvs.f_bsize * stvs.f_bfree	/ 1024;
+			part.nKiBUsed = part.nKiBSize - part.nKiBFree;
+		}
+		else
+		{
+			TRACE("statvfs failed on \"%s\": %s\n", part.szMntPoint, strerror(errno));
+			memset(part.szMntPoint, 0, sizeof(part.szMntPoint));
+			part.nKiBSize = 0;
+			part.nKiBFree = 0;
+			part.nKiBUsed = 0;
+		}
+
+		return true;
+	}
+	else
+	{
+		memset(part.szMntPoint, 0, sizeof(part.szMntPoint));
+		part.nKiBSize = 0;
+		part.nKiBFree = 0;
+		part.nKiBUsed = 0;
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static std::string _StrReplace(std::string &s, const char *pszFind, const char *pszRepl)
+{
+	std::string r = s;
+
+	if(pszFind && *pszFind && pszRepl)
+	{
+		size_t nFind, nLen = strlen(pszFind);
+
+		while((nFind = r.find(pszFind)) !=  std::string::npos)
+		{
+			r = r.replace(nFind, nLen, pszRepl);
+		}
+	}
+	
+	return r;
+}
+	
+static std::string _UnescapeMountpointString(std::string &s)
+{
+	std::string r = s;
+	r = _StrReplace(r, "\\040", " ");
+	r = _StrReplace(r, "\\011", "\t");
+	r = _StrReplace(r, "\\012", "\n");
+	r = _StrReplace(r, "\\134", "\\");
+	return r;
+}
+
+static void _UpdateMountMap(MountMap &mm)
+{
+	char szLine[512], szNode[512], szMount[512];
+	std::ifstream mounts(_MOUNTS_FILE);
+	mm.clear();
+
+	while(mounts.getline(szLine, sizeof(szLine)).good())
+	{
+		if((sscanf(szLine, "%s %s", szNode, szMount) == 2))
+		{
+			std::string key(szNode);
+			key = _UnescapeMountpointString(key);
+			std::string val(szMount);
+			val = _UnescapeMountpointString(val);
+			mm.emplace(key, val);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool _ProcessMounts(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm)
+{
+	bool bChange = false;
+	char szMntPoint[_countof(GFA_SYSINFO_PARTITION::szMntPoint)];
+
+	for(size_t i = 0; i < _countof(sdm.parts); ++i)
+	{
+		GFA_SYSINFO_PARTITION &part = sdm.parts[i];
+
+		if(part.valid)
+		{
+			bool bMountedOld = !!*part.szMntPoint;
+			bool bMountedNew = _LookupMountPoint(mm, part.szDevNode, szMntPoint, sizeof(szMntPoint));
+
+			if(!bMountedOld && bMountedNew)
+			{
+				memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint));
+				_UpdatePartitionFsInfo(mm, part);
+				_SetPartitionChange(sdm, i);
+				bChange = true;
+			}
+			else if(bMountedOld && !bMountedNew)
+			{
+				memset(part.szMntPoint, 0, sizeof(part.szMntPoint));
+				_UpdatePartitionFsInfo(mm, part);
+				_SetPartitionChange(sdm, i);
+				bChange = true;
+			}
+			else if(bMountedOld && bMountedNew)
+			{
+				if(strcmp(part.szMntPoint, szMntPoint))
+				{
+					memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint));
+					_UpdatePartitionFsInfo(mm, part);
+					_SetPartitionChange(sdm, i);
+					bChange = true;
+				}
+			}
+		}
+	}
+
+	return bChange;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const char* _GetFileSpec(const char *pszPathname)
+{
+	if(pszPathname && *pszPathname)
+	{
+		const char *pSlash =  strrchr(pszPathname, '/');
+		if(pSlash)
+			return  ++pSlash;
+		else
+			return pszPathname;
+	}
+
+	return NULL;
+}
+
+static bool _IsInternalEmmc(const char *pszDevNode)
+{
+	if(pszDevNode)
+	{
+		const char *pszDevName = _GetFileSpec(pszDevNode);
+		return _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART2) || _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART1);
+	}
+
+	return false;
+}
+
+static long long _NumberFromString(const char *pszString, int base, bool *pbErr)
+{
+	if(!pszString || !*pszString)
+	{
+		if(pbErr)
+			*pbErr = true;
+		return 0;
+	}
+
+	char *endptr;
+	long long nRet = strtoll(pszString, &endptr, base);
+
+	if( (((nRet == LLONG_MAX) || (nRet == LLONG_MIN)) && (errno == ERANGE)) ||
+		((nRet == 0) && (errno == EINVAL)) ||
+		(!!*endptr))
+	{
+		if(pbErr)
+			*pbErr = true;
+		return 0;
+	}
+
+	if(pbErr)
+		*pbErr = false;
+	return nRet;
+}
+
+static long long _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, int base, bool *pbErr)
+{
+	char szNum[64];
+	return _NumberFromString(_ReadDevPropertyValue(dev, pszKey, szNum, sizeof(szNum), false), base, pbErr);
+}
+
+static const char* _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, char *pszValue, size_t nCChValue, bool bTruncate)
+{
+	if(!pszValue || !nCChValue)
+	    return NULL;
+   memset(pszValue, 0, nCChValue);
+
+    const char *pszVal = ::udev_device_get_property_value(dev, pszKey);
+
+    if(pszVal)
+    {
+    	size_t nLen = strlen(pszVal);
+
+    	if(nLen < nCChValue)
+    	{
+    		strcpy(pszValue, pszVal);
+    		return pszValue;
+    	}
+    	else if(bTruncate)
+    	{
+    		memcpy(pszValue, pszVal, nCChValue);
+    		pszValue[nCChValue - 1] = '\0';
+    		return pszValue;
+    	}
+    }
+
+    return NULL;
+}
+
+static void _ProcessPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm, struct udev_device* dev)
+{
+	if(dev)
+	{
+		bool bChange;
+		GFA_SYSINFO_PARTITION part;
+		memset(&part, 0, sizeof(part));
+		part.nDiskIdx = -1;
+		int nPartIdx = -1;
+		int nDiskIdx = -1;
+
+		const char *pszDevNode = ::udev_device_get_devnode(dev);
+
+		if(!pszDevNode)
+			return;
+//		if(_IsInternalEmmc(pszDevNode))
+//			return;	// skip internal emmc
+		bool bInternalEmmc = _IsInternalEmmc(pszDevNode);
+		strncpy(part.szDevNode, pszDevNode, sizeof(part.szDevNode) - 1);
+
+		const char *pszAction = ::udev_device_get_action(dev);
+		bool bAdd = _STR_EQUALS(pszAction, "add");
+		bool bRem = _STR_EQUALS(pszAction, "remove");
+		bool bEnum = !pszAction;
+
+		if(bAdd || bEnum)
+		{
+			if(_LookupPartition(sdm, part.szDevNode) >= 0)
+				return;
+
+			part.internal = bInternalEmmc;
+			_ReadDevPropertyValue(dev, "ID_FS_LABEL", part.szFsLabel, sizeof(part.szFsLabel), true);
+			_ReadDevPropertyValue(dev, "ID_FS_TYPE", part.szFsType, sizeof(part.szFsType), true);
+			_ReadDevPropertyValue(dev, "ID_FS_VERSION", part.szFsVersion, sizeof(part.szFsVersion), true);
+			part.nKiBPartSize = _ReadDevPropertyValue(dev, "ID_PART_ENTRY_SIZE") / 2;
+			_UpdatePartitionFsInfo(mm, part);
+
+			struct udev_device* cur = dev;
+
+			while((cur = ::udev_device_get_parent(cur)))
+			{
+				const char *pszSs = ::udev_device_get_subsystem(cur);
+				const char *pszDt = ::udev_device_get_devtype(cur);
+				const char *pszDn = ::udev_device_get_devnode(cur);
+
+				if(!pszDn)
+					break;
+
+				if(_STR_EQUALS(pszSs, "usb") || _STR_EQUALS(pszSs, "block"))
+				{
+					if(_STR_EQUALS(pszDt, "disk"))
+					{
+						GFA_SYSINFO_DISK disk;
+						memset(&disk, 0, sizeof(disk));
+						strncpy(disk.szDevNode, pszDn, sizeof(disk.szDevNode) - 1);
+
+						disk.internal = bInternalEmmc;
+						if(!_ReadDevPropertyValue(cur, "ID_NAME", disk.szName, sizeof(disk.szName), true))
+							_ReadDevPropertyValue(cur, "ID_MODEL", disk.szName, sizeof(disk.szName), true);
+						_ReadDevPropertyValue(cur, "ID_VENDOR", disk.szVendor, sizeof(disk.szVendor), true);
+						_ReadDevPropertyValue(cur, "ID_BUS", disk.szBus, sizeof(disk.szBus), true);
+
+						disk.nVendorID	= _ReadDevPropertyValue(cur, "ID_VENDOR_ID", 16);
+						disk.nProductID	= _ReadDevPropertyValue(cur, "ID_MODEL_ID", 16);
+
+						if((nDiskIdx = _AddDisk(sdm, disk, bChange)) >= 0)
+						{
+							if((nPartIdx = _AddPartition(sdm, part, bChange)) < 0)
+							{
+								if(sdm.disks[nDiskIdx].nPartCount == 0)
+									_RemoveDisk(sdm, nDiskIdx, bChange);
+								break;
+							}
+							_DiskAddPartition(sdm, nDiskIdx, nPartIdx, bChange);
+							_PartitionSetDisk(sdm, nPartIdx, nDiskIdx, bChange);
+							_SetPartitionChange(sdm, nPartIdx);
+						}
+
+						break;
+					}
+				}
+			}
+		}
+		else if(bRem)
+		{
+			if((nPartIdx = _LookupPartition(sdm, pszDevNode)) >= 0)
+			{
+				_RemovePartition(sdm, nPartIdx, bChange);
+				_SetPartitionChange(sdm, nPartIdx);
+
+				if((nDiskIdx = _PartitionGetDisk(sdm, nPartIdx)) >= 0)
+				{
+					if(!_DiskRemovePartition(sdm, nDiskIdx, nPartIdx, bChange))
+					{
+						_RemoveDisk(sdm, nDiskIdx, bChange);
+					}
+				}
+			}
+		}
+	}
+}
+
+static void _EnumStorageDevices(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm, struct udev *pUdev)
+{
+	struct udev_enumerate *pEnum = ::udev_enumerate_new(pUdev);
+	::udev_enumerate_add_match_subsystem(pEnum, "block");
+	::udev_enumerate_add_match_property(pEnum, "DEVTYPE", "partition");
+	::udev_enumerate_scan_devices(pEnum);
+
+	struct udev_list_entry *devices = ::udev_enumerate_get_list_entry(pEnum);
+	struct udev_list_entry *entry;
+
+	udev_list_entry_foreach(entry, devices)
+	{
+		const char *pszPath = ::udev_list_entry_get_name(entry);
+		struct udev_device* dev = ::udev_device_new_from_syspath(pUdev, pszPath);
+
+		_ProcessPartition(sdm, mm, dev);
+		::udev_device_unref(dev);
+	}
+
+	::udev_enumerate_unref(pEnum);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+CStgDevInfo::CStgDevInfo(void) : m_bPaused(false)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void* CStgDevInfo::ThreadRoutine(void *pParam)
+{
+	LPEXEC_PARAMS pep = (LPEXEC_PARAMS)pParam;
+
+	if(pep)
+	{
+		bool bRun = false;
+		int nRet, nSig;
+		struct udev *pUdev = NULL;
+		struct udev_monitor *pUdevMon = NULL;
+		GFA_SYSINFO_STORAGE_DEVICE_MAP sdm;
+		memset(&sdm, 0, sizeof(sdm));
+		MountMap mm;
+		pollfd pfd[2];
+		memset(&pfd, 0, sizeof(pfd));
+		
+		do
+		{
+		    if(!(pUdev = ::udev_new()))
+		    {
+		        TRACE("udev_new failed\n");
+				break;
+		    }
+
+			_UpdateMountMap(mm);
+			_EnumStorageDevices(sdm, mm, pUdev);
+
+			if(!(pUdevMon = ::udev_monitor_new_from_netlink(pUdev, "udev")))
+			{
+		        TRACE("udev_monitor_new_from_netlink failed\n");
+				break;
+			}
+
+			if(::udev_monitor_filter_add_match_subsystem_devtype(pUdevMon, "block", "partition") < 0)
+			{
+		        TRACE("udev_monitor_filter_add_match_subsystem_devtype failed\n");
+				break;
+			}
+
+			if(::udev_monitor_enable_receiving(pUdevMon) < 0)
+			{
+		        TRACE("udev_monitor_enable_receiving failed\n");
+				break;
+			}
+
+			pfd[0].fd = ::udev_monitor_get_fd(pUdevMon);
+			pfd[0].events = POLLIN;
+			pfd[0].revents = 0;
+			pfd[1].fd = open(_MOUNTS_FILE, O_RDONLY, 0);
+			pfd[1].events = POLLPRI;
+			pfd[1].revents = 0;
+
+			bRun = true;
+			::GfaIpcAppCtrlUpdateStorageDeviceMap(pep->hAC, sdm);
+			sleep(1);
+		}
+		while(false);
+
+		while(bRun)
+		{
+			if(!m_bPaused)
+			{
+				if((nRet = WaitSignalTimeout(0, &nSig)) == ETIMEDOUT)
+				{
+					_ClearMapChanges(sdm);
+
+					while(poll(pfd, 2, 500) > 0)
+					{
+						_UpdateMountMap(mm);
+
+						if(pfd[0].revents & POLLIN)
+						{
+							struct udev_device* dev;
+
+							while((dev = ::udev_monitor_receive_device(pUdevMon)))
+							{
+								_ProcessPartition(sdm, mm, dev);
+							}
+						}
+
+						if(pfd[1].revents & POLLPRI)
+						{
+							_ProcessMounts(sdm, mm);
+						}
+					}
+
+					if(_DeviceMapChanged(sdm))
+					{
+						::GfaIpcAppCtrlUpdateStorageDeviceMap(pep->hAC, sdm);
+					}
+				}
+			}
+			else if(!nRet) // signal received
+			{
+				TRACE("%s signal %d received.\n", __FUNCTION__, nSig);
+
+				switch(nSig)
+				{
+				case S_Init:
+					break;
+				case S_Update:
+					break;
+				case S_Pause:
+					m_bPaused = true;
+					break;
+				case S_Resume:
+					m_bPaused = false;
+					break;
+				case S_Terminate:
+					bRun = false;
+					break;
+				default:
+					break;
+				}
+			}
+			else
+			{
+				TRACE("%s error %d.\n", __FUNCTION__, nRet);
+			}
+		}
+
+		if(pfd[0].fd >= 0)
+			close(pfd[0].fd);
+		if(pfd[1].fd >= 0)
+			close(pfd[1].fd);
+		if(pUdevMon)
+			::udev_monitor_unref(pUdevMon);
+		if(pUdev)
+			::udev_unref(pUdev);
+	}
+
+	TRACE("%s exit.\n", __FUNCTION__);
+	return NULL;
+}

+ 44 - 0
gfasysinfo/src/stgdevinfo.h

@@ -0,0 +1,44 @@
+// stgdevinfo.h :
+//
+
+#if !defined(AGD_STGDEVINFO_H__42237980_CC43_484B_8AAC_425F05E77063__INCLUDED_)
+#define AGD_STGDEVINFO_H__42237980_CC43_484B_8AAC_425F05E77063__INCLUDED_
+
+#include <gfa/gfaipc.h>
+#include <gfa/ipcpriv.h>
+#include <gfa/thread.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// stgdevinfo.h - Declarations:
+
+class CStgDevInfo : public CThread
+{
+public:
+	typedef enum
+	{
+		S_Init = 1,
+		S_Update,
+		S_Pause,
+		S_Resume,
+		S_Terminate
+	}Signals;
+
+public:
+	typedef struct _EXEC_PARAMS
+	{
+		HAPPCTRL hAC;
+	}EXEC_PARAMS, *LPEXEC_PARAMS;
+	typedef const EXEC_PARAMS *LPCEXEC_PARAMS;
+
+public:
+	CStgDevInfo(void);
+
+protected:
+	virtual void* ThreadRoutine(void *pParam);
+
+private:
+	bool m_bPaused;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_STGDEVINFO_H__42237980_CC43_484B_8AAC_425F05E77063__INCLUDED_)

+ 125 - 0
src/appctrl.cpp

@@ -267,6 +267,16 @@ appid_t CAppCtrl::GetRunningAppsMask(void)
 	return mask;
 }
 
+void CAppCtrl::TriggertHeartBeat(void)
+{
+	struct timespec	tsCur;
+	APP_CTRL_PROCESS &proc	= m_pAppCtrl->proc[m_nSlotIdx];
+	::clock_gettime(CLOCK_MONOTONIC, &tsCur);
+	CAppCtrlShmLocker locker(*this);
+	proc.nHeartbeatLastUs = proc.nHeartbeatCurUs;
+	proc.nHeartbeatCurUs = _TIMESPEC_2_US(tsCur);
+}
+
 /////////////////////////////////////////////////////////////////////////////
 
 LPAPP_CTRL_INFO CAppCtrl::AppInfoUpdate(clock64_t nCurWorkingTime)
@@ -280,8 +290,10 @@ LPAPP_CTRL_INFO CAppCtrl::AppInfoUpdate(clock64_t nCurWorkingTime)
 
 		m_ai.nStateEvtPending	= proc.nStateEvtPending;
 		m_ai.nCtrlMsgPending	= proc.nCtrlMsgPending;
+		m_ai.nSysEvtPending		= proc.nSysEvtPending & ~GFA_APPCTRL_SYSEVENT_ALL_STG_DEV;
 		proc.nStateEvtPending	= 0;
 		proc.nCtrlMsgPending	= 0;
+		proc.nSysEvtPending		= 0;
 
 		if(sys.nPidInCharge)
 		{
@@ -656,6 +668,7 @@ bool CAppCtrl::SubscribeSysEvents(sysevt_t nEvtMask)
 		CAppCtrlShmLocker locker(*this);
 		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
 		proc.nSysEvtRegistered |= nEvtMask;
+		proc.nSysEvtPending |= (nEvtMask & GFA_APPCTRL_SYSEVENT_ALL_MYSQL);
 		return true;
 	}
 
@@ -669,6 +682,7 @@ bool CAppCtrl::UnsubscribeSysEvents(sysevt_t nEvtMask)
 		CAppCtrlShmLocker locker(*this);
 		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
 		proc.nSysEvtRegistered &= ~nEvtMask;
+		proc.nSysEvtPending &= ~nEvtMask;
 		return true;
 	}
 
@@ -842,6 +856,23 @@ bool CAppCtrl::GetBootFromEmmc(bool *pbBootFromEmmc)
 	return false;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+
+bool CAppCtrl::GetDbInfo(LPGFA_SYSINFO_DATABASE psdb)
+{
+	if(m_hShm && psdb)
+	{
+		GFA_SYSINFO_DATABASE &sdb = m_pAppCtrl->sdb;
+		CAppCtrlShmLocker locker(*this);
+		memcpy(psdb, &sdb, sizeof(GFA_SYSINFO_DATABASE));
+		return true;
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 bool CAppCtrl::KillApp(appid_t nAppID)
 {
 	if(m_hShm && _IS_VALID_APP_ID(nAppID))
@@ -1202,6 +1233,73 @@ int CAppCtrl::GetNextMountRemoved(LPGFA_SYSINFO_PARTITION pPart)
 
 /////////////////////////////////////////////////////////////////////////////
 
+bool CAppCtrl::UpdateDbInfo(const GFA_SYSINFO_DATABASE &sdb)
+{
+	if(m_hShm)
+	{
+		sysevt_t evt = 0;
+		
+		GFA_SYSINFO_DATABASE &rsdb = m_pAppCtrl->sdb;
+		CAppCtrlShmLocker locker(*this);
+		
+		if(rsdb.bIsInit != sdb.bIsInit)
+		{
+			evt |= GFA_APPCTRL_SYSEVENT_MYSQL_INIT_EVT;
+			rsdb.bIsInit = sdb.bIsInit;
+		}
+		
+		if(sdb.bIsInit)
+		{
+			if(memcmp(&rsdb.svr, &sdb.svr, sizeof(GFA_MYSQL_SERVER)))
+			{
+				evt |= GFA_APPCTRL_SYSEVENT_MYSQL_SERVER_EVT;
+				memcpy(&rsdb.svr, &sdb.svr, sizeof(GFA_MYSQL_SERVER));
+			}
+
+			if(memcmp(&rsdb.res, &sdb.res, sizeof(GFA_MYSQL_RESOURCE)))
+			{
+				evt |= GFA_APPCTRL_SYSEVENT_MYSQL_RESOURCE_EVT;
+				memcpy(&rsdb.res, &sdb.res, sizeof(GFA_MYSQL_RESOURCE));
+			}
+
+			if(rsdb.nNumDatabases != sdb.nNumDatabases)
+			{
+				rsdb.nNumDatabases = sdb.nNumDatabases;
+				evt |= GFA_APPCTRL_SYSEVENT_MYSQL_DATABASE_EVT;
+				memcpy(&rsdb.dbs, &sdb.dbs, sizeof(GFA_MYSQL_SCHEMA) * GFA_MYSQL_MAX_DATABASES);
+			}
+			else if(memcmp(&rsdb.dbs, &sdb.dbs, sizeof(GFA_MYSQL_SCHEMA) * GFA_MYSQL_MAX_DATABASES))
+			{
+				evt |= GFA_APPCTRL_SYSEVENT_MYSQL_DATABASE_EVT;
+				memcpy(&rsdb.dbs, &sdb.dbs, sizeof(GFA_MYSQL_SCHEMA) * GFA_MYSQL_MAX_DATABASES);
+			}
+		}
+		else
+		{
+			memset(&rsdb, 0, sizeof(GFA_SYSINFO_DATABASE));
+		}
+
+		if(evt)
+		{
+			for(int i = 0; i < _APP_CTRL_MAX_SLOTS; ++i)
+			{
+				APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[i];
+				if(_IS_VALID_APP_ID(proc.nAppID) && (proc.nSysEvtRegistered & GFA_APPCTRL_SYSEVENT_ALL_MYSQL))
+				{
+					evt &= proc.nSysEvtRegistered;
+					proc.nSysEvtPending |= evt;
+				}
+			}
+		}
+		
+		return true;
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 int CAppCtrl::SlotIndexFromAppID(appid_t nAppID)
 {
 	if(nAppID == 0)
@@ -1495,6 +1593,25 @@ extern "C" bool GfaIpcAppCtrlUpdateStorageDeviceMap(HAPPCTRL hAC, const GFA_SYSI
 	return false;
 }
 
+extern "C" bool GfaIpcAppCtrlUpdateDbInfo(HAPPCTRL hAC, const GFA_SYSINFO_DATABASE &sdb)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->UpdateDbInfo(sdb);
+	return false;
+}
+
+extern "C" bool GfaIpcAppCtrlTriggertHeartBeat(HAPPCTRL hAC)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+	{
+		p->TriggertHeartBeat();
+		return true;
+	}
+	return false;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -1571,6 +1688,14 @@ bool GfaIpcAppCtrlGetBootFromEmmc(HAPPCTRL hAC, bool *pbBootFromEmmc)
 	return false;
 }
 
+bool GfaIpcAppCtrlGetDbInfo(HAPPCTRL hAC, LPGFA_SYSINFO_DATABASE psdb)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetDbInfo(psdb);
+	return false;
+}
+
 bool GfaIpcAppCtrlKillApp(HAPPCTRL hAC, appid_t nAppID)
 {
 	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);

+ 14 - 12
src/appctrl.h

@@ -28,8 +28,6 @@
 #define _UUID_SHM_APP_CTRL								_UUID_SHM_APP_CTRL_V1
 #define _NAME_SHM_APP_CTRL								_NAME_SHM_APP_CTRL_V1
 
-//#define _USB_DEVICE_LIST_MAX_ENTRIES					4
-
 /////////////////////////////////////////////////////////////////////////////
 
 typedef struct _APP_CTRL_INFO
@@ -44,15 +42,15 @@ typedef const APP_CTRL_INFO *LPCAPP_CTRL_INFO;
 
 typedef struct _APP_CTRL_SYSTEM
 {
-	pid_t nPidInCharge;
-	bool bBootFromEmmc;
-	time_t nTsSystemStart;
-	clock64_t nClkLastUpdate;
-	int nUpdateError;
+	pid_t		nPidInCharge;
+	bool		bBootFromEmmc;
+	time_t		nTsSystemStart;
+	clock64_t	nClkLastUpdate;
+	int			nUpdateError;
 	GFA_APPCTRL_SYSMEM mem;
-	uint32_t nDiskMask;
-	uint32_t nPartMask;
-	uint32_t nMountMask;
+	uint32_t	nDiskMask;
+	uint32_t	nPartMask;
+	uint32_t	nMountMask;
 	GFA_SYSINFO_STORAGE_DEVICE_MAP sdm;
 	GFA_SYSINFO_SPI spi;
 }APP_CTRL_SYSTEM, *LPAPP_CTRL_SYSTEM;
@@ -91,8 +89,9 @@ typedef const APP_CTRL_PROCESS *LPCAPP_CTRL_PROCESS;
 
 typedef struct _APP_CTRL
 {
-	APP_CTRL_SYSTEM		sys;
-	APP_CTRL_PROCESS	proc[_APP_CTRL_MAX_SLOTS];
+	APP_CTRL_SYSTEM			sys;
+	GFA_SYSINFO_DATABASE	sdb;
+	APP_CTRL_PROCESS		proc[_APP_CTRL_MAX_SLOTS];
 }APP_CTRL, *LPAPP_CTRL;
 typedef const APP_CTRL *LPCAPP_CTRL;
 
@@ -127,6 +126,7 @@ public:
 
 	appid_t GetRunningAppsMask(void);
 	LPAPP_CTRL_INFO AppInfoUpdate(clock64_t nCurWorkingTime);
+	void TriggertHeartBeat(void);
 
 	GfaIpcAppStates GetAppState(appid_t nAppID);
 	GfaIpcAppStates SetAppState(GfaIpcAppStates state);
@@ -150,6 +150,7 @@ public:
 	bool ReleaseSysInfo(void);
 	bool UpdateSysInfo(void);
 	bool UpdateStorageDeviceMap(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm);
+	bool UpdateDbInfo(const GFA_SYSINFO_DATABASE &sdb);
 
 	bool GetSysMem(LPGFA_APPCTRL_SYSMEM psm);
 	bool GetSysSPI(LPGFA_SYSINFO_SPI pspi);
@@ -166,6 +167,7 @@ public:
 	int GetNextMountRemoved(LPGFA_SYSINFO_PARTITION pPart);
 	
 	bool GetBootFromEmmc(bool *pbBootFromEmmc);
+	bool GetDbInfo(LPGFA_SYSINFO_DATABASE psdb);
 	
 	bool KillApp(appid_t nAppID);
 

+ 82 - 3
src/gfaipc.h

@@ -51,6 +51,20 @@ typedef unsigned long long							sysevt_t;
 #define GFA_APPCTRL_MAKE_CTRLMSG(m)					(1ULL << (m))
 #define GFA_APPCTRL_MAKE_SYSEVENT(e)				(1ULL << (e))
 
+#define GFA_MYSQL_MAX_DB_NAME_LENGTH				64
+#define GFA_MYSQL_MAX_DB_USER_LENGTH				64
+#define GFA_MYSQL_MAX_DB_PASS_LENGTH				64
+#define GFA_MYSQL_MAX_TABLE_NAME_LENGTH				64
+#define GFA_MYSQL_MAX_ENGINE_NAME_LENGTH			32
+#define GFA_MYSQL_MAX_ROW_FORMAT_LENGTH				32
+#define GFA_MYSQL_MAX_COLLATION_LENGTH				32
+
+#define GFA_MYSQL_MAX_SVR_VERSION_LENGTH			32
+#define GFA_MYSQL_MAX_DATADIR_LENGTH				64
+
+#define GFA_MYSQL_MAX_DATABASES						8
+#define GFA_MYSQL_MAX_TABLES_PER_DATABASE			12
+
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
 // GfA
@@ -166,9 +180,15 @@ typedef enum
 #define GFA_APPCTRL_SYSEVENT_DISK_EVT				GFA_APPCTRL_MAKE_SYSEVENT(0)
 #define GFA_APPCTRL_SYSEVENT_PART_EVT				GFA_APPCTRL_MAKE_SYSEVENT(1)
 #define GFA_APPCTRL_SYSEVENT_MOUNT_EVT				GFA_APPCTRL_MAKE_SYSEVENT(2)
-
 #define GFA_APPCTRL_SYSEVENT_ALL_STG_DEV			(GFA_APPCTRL_SYSEVENT_DISK_EVT | GFA_APPCTRL_SYSEVENT_PART_EVT | GFA_APPCTRL_SYSEVENT_MOUNT_EVT)
 
+#define GFA_APPCTRL_SYSEVENT_MYSQL_INIT_EVT			GFA_APPCTRL_MAKE_SYSEVENT(3)
+#define GFA_APPCTRL_SYSEVENT_MYSQL_SERVER_EVT		GFA_APPCTRL_MAKE_SYSEVENT(4)
+#define GFA_APPCTRL_SYSEVENT_MYSQL_RESOURCE_EVT		GFA_APPCTRL_MAKE_SYSEVENT(5)
+#define GFA_APPCTRL_SYSEVENT_MYSQL_DATABASE_EVT		GFA_APPCTRL_MAKE_SYSEVENT(6)
+#define GFA_APPCTRL_SYSEVENT_ALL_MYSQL				(GFA_APPCTRL_SYSEVENT_MYSQL_INIT_EVT | GFA_APPCTRL_SYSEVENT_MYSQL_SERVER_EVT | GFA_APPCTRL_SYSEVENT_MYSQL_RESOURCE_EVT | GFA_APPCTRL_SYSEVENT_MYSQL_DATABASE_EVT)
+
+
 /////////////////////////////////////////////////////////////////////////////
 
 #define GFA_APPCTRL_MAX_DISKS						8
@@ -283,15 +303,73 @@ typedef struct _GFA_SYSINFO_SPI
 {
 	float fVoltagePowerSupply;
 	float fVoltageBackupBattery;
-
 	float fVoltageSys;
 	float fVoltageBattery;
-
 	float fTemperatureBoard;
 	float fTemperatureTiva;
 }GFA_SYSINFO_SPI, *LPGFA_SYSINFO_SPI;
 typedef const GFA_SYSINFO_SPI *LPCGFA_SYSINFO_SPI;
 
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_MYSQL_SERVER
+{
+	bool bRunning;
+	bool bInnoDbFilePerTable;
+	pid_t pid;
+	time_t nUptimeSec;
+	size_t nDiscUsageTotal;
+	char szVersion[GFA_MYSQL_MAX_SVR_VERSION_LENGTH];
+	char szDataDir[GFA_MYSQL_MAX_DATADIR_LENGTH];
+}GFA_MYSQL_SERVER, *LPGFA_MYSQL_SERVER;
+typedef const GFA_MYSQL_SERVER *LPCGFA_MYSQL_SERVER;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_MYSQL_RESOURCE
+{
+	GFA_APPCTRL_APPTIMES	at;
+	GFA_APPCTRL_APPMEM		am;
+}GFA_MYSQL_RESOURCE, *LPGFA_MYSQL_RESOURCE;
+typedef const GFA_MYSQL_RESOURCE *LPCGFA_MYSQL_RESOURCE;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_MYSQL_TABLE
+{
+	char szName[GFA_MYSQL_MAX_TABLE_NAME_LENGTH];
+	char szEngine[GFA_MYSQL_MAX_ENGINE_NAME_LENGTH];
+	char szRowFormat[GFA_MYSQL_MAX_ROW_FORMAT_LENGTH];
+	char szCollation[GFA_MYSQL_MAX_COLLATION_LENGTH];
+	int nVersion;
+	time_t nCreateTime;
+	size_t nSizeTotal;
+}GFA_MYSQL_TABLE, *LPGFA_MYSQL_TABLE;
+typedef const GFA_MYSQL_TABLE *LPCGFA_MYSQL_TABLE;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_MYSQL_SCHEMA
+{
+	size_t nSizeTotal;
+	unsigned int nNumTables;
+	char szName[GFA_MYSQL_MAX_DB_NAME_LENGTH];
+	GFA_MYSQL_TABLE tables[GFA_MYSQL_MAX_TABLES_PER_DATABASE];
+}GFA_MYSQL_SCHEMA, *LPGFA_MYSQL_SCHEMA;
+typedef const GFA_MYSQL_SCHEMA *LPCGFA_MYSQL_SCHEMA;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_SYSINFO_DATABASE
+{
+	bool bIsInit;										// fires GFA_APPCTRL_SYSEVENT_MYSQL_INIT_EVT
+	unsigned int nNumDatabases;
+	GFA_MYSQL_SERVER	svr;							// fires GFA_APPCTRL_SYSEVENT_MYSQL_SERVER_EVT
+	GFA_MYSQL_RESOURCE	res;							// fires GFA_APPCTRL_SYSEVENT_MYSQL_RESOURCE_EVT
+	GFA_MYSQL_SCHEMA	dbs[GFA_MYSQL_MAX_DATABASES];	// fires GFA_APPCTRL_SYSEVENT_MYSQL_DATABASE_EVT
+}GFA_SYSINFO_DATABASE, *LPGFA_SYSINFO_DATABASE;
+typedef const GFA_SYSINFO_DATABASE *LPCGFA_SYSINFO_DATABASE;
+
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
 
@@ -333,6 +411,7 @@ int				GfaIpcAppCtrlGetNextMountAdded		(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pP
 int				GfaIpcAppCtrlGetNextMountRemoved	(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart);
 
 bool			GfaIpcAppCtrlGetBootFromEmmc		(HAPPCTRL hAC, bool *pbBootFromEmmc);
+bool			GfaIpcAppCtrlGetDbInfo				(HAPPCTRL hAC, LPGFA_SYSINFO_DATABASE psdb);
 
 /////////////////////////////////////////////////////////////////////////////
 

+ 2 - 0
src/ipcpriv.h

@@ -30,6 +30,8 @@ bool GfaIpcAppCtrlReleaseSysInfo(HAPPCTRL hAC);
 bool GfaIpcAppCtrlUpdateSysInfo(HAPPCTRL hAC);
 bool GfaIpcAppCtrlUpdateStorageDeviceMap(HAPPCTRL hAC, const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm);
 void GfaIpcForceReleaseMutex(void);
+bool GfaIpcAppCtrlUpdateDbInfo(HAPPCTRL hAC, const GFA_SYSINFO_DATABASE &sdb);
+bool GfaIpcAppCtrlTriggertHeartBeat(HAPPCTRL hAC);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 171 - 0
src/thread.cpp

@@ -0,0 +1,171 @@
+#include <errno.h>
+#include "thread.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define UNUSED(v)							(void)v
+
+/////////////////////////////////////////////////////////////////////////////
+
+CThread::CThread(void) : m_tID(0),	m_tCond(PTHREAD_COND_INITIALIZER),
+									m_tMtxCond(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP),
+									m_vSigs(),
+									m_nCondSig(-1),
+									m_bSignaled(false),
+									m_bBlocked(false)									
+{
+	::pthread_attr_init(&m_tAttr);
+	m_itp.pThis = this;
+}
+
+CThread::~CThread(void)
+{
+	::pthread_detach(m_tID);
+	::pthread_attr_destroy(&m_tAttr);
+	::pthread_cond_destroy(&m_tCond);
+	::pthread_mutex_destroy(&m_tMtxCond);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int CThread::Create(PFN_THREAD_ROUTINE pFunc, void *pParam)
+{
+	return ::pthread_create(&m_tID, &m_tAttr, pFunc, pParam);
+}
+
+int CThread::Create(void *pParam)
+{
+	m_itp.pParam = pParam;
+	return ::pthread_create(&m_tID, &m_tAttr, &CThread::_ThreadStart, &m_itp);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int CThread::Cancel(void)
+{
+	return ::pthread_cancel(m_tID);
+}
+
+int CThread::Join(void **ppRetval)
+{
+	return ::pthread_join(m_tID, ppRetval);
+}
+
+int CThread::Detach(void)
+{
+	return ::pthread_detach(m_tID);
+}
+
+int CThread::WaitSignal(int *pSignal)
+{
+	if(!pSignal)
+		return EINVAL;
+	int nRet;
+	::pthread_mutex_lock(&m_tMtxCond);
+	if(m_vSigs.size() > 0)
+	{
+		auto i = m_vSigs.erase(m_vSigs.begin());
+		*pSignal = *i;
+		nRet = 0;
+	}
+	else
+	{
+		m_bBlocked = true;
+		do {
+			nRet = ::pthread_cond_wait(&m_tCond, &m_tMtxCond);
+		}while(!nRet && !m_bSignaled);
+		m_bBlocked = false;
+		*pSignal = !nRet ? m_nCondSig : -nRet;
+		m_bSignaled = false;
+	}
+	::pthread_mutex_unlock(&m_tMtxCond);
+	return nRet;
+}
+
+int CThread::WaitSignalTimeout(unsigned long long nUs, int *pSignal)
+{
+	if(!pSignal)
+		return EINVAL;
+	int nRet;
+	::pthread_mutex_lock(&m_tMtxCond);
+	if(m_vSigs.size() > 0)
+	{
+		auto i = m_vSigs.erase(m_vSigs.begin());
+		*pSignal = *i;
+		nRet = 0;
+	}
+	else if(nUs)
+	{
+		struct timespec tsc;
+		unsigned long long ts;
+		::clock_gettime(CLOCK_REALTIME, &tsc);
+		ts = (unsigned long long)tsc.tv_sec * 1000000000ULL + (unsigned long long)tsc.tv_nsec + nUs * 1000ULL;
+		tsc.tv_sec = ts / 1000000000ULL;
+		tsc.tv_nsec = ts % 1000000000ULL;
+		m_bBlocked = true;
+		do {
+			nRet = ::pthread_cond_timedwait(&m_tCond, &m_tMtxCond, &tsc);
+		}while(!nRet && !m_bSignaled);
+		m_bBlocked = false;
+		*pSignal = !nRet ? m_nCondSig : -nRet;
+		m_bSignaled = false;
+	}
+	else
+	{
+		*pSignal = -1;
+		nRet = ETIMEDOUT;
+	}
+	::pthread_mutex_unlock(&m_tMtxCond);
+	return nRet;
+}
+
+int CThread::Signal(int nSig)
+{
+	int nRet;
+	::pthread_mutex_lock(&m_tMtxCond);
+	if(m_bBlocked)
+	{
+		m_nCondSig = nSig;
+		nRet = ::pthread_cond_signal(&m_tCond);
+		m_bSignaled = true;
+	}
+	else
+	{
+		m_nCondSig = -1;
+		m_vSigs.push_back(nSig);
+	}
+	::pthread_mutex_unlock(&m_tMtxCond);
+	return nRet;
+}
+
+bool CThread::PopSignal(int *pSignal)
+{
+	if(!pSignal)
+		return false;
+	bool bRet = false;
+	::pthread_mutex_lock(&m_tMtxCond);
+	if(m_vSigs.size() > 0)
+	{
+		auto i = m_vSigs.erase(m_vSigs.begin());
+		*pSignal = *i;
+		bRet = true;
+	}
+	::pthread_mutex_unlock(&m_tMtxCond);
+	return bRet;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void* CThread::_ThreadStart(void *pParam)
+{
+	LPINTTP ptp = (LPINTTP)pParam;
+	return ptp->pThis->ThreadRoutine(ptp->pParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void* CThread::ThreadRoutine(void *pParam)
+{
+	UNUSED(pParam);
+	return NULL;
+}

+ 66 - 0
src/thread.h

@@ -0,0 +1,66 @@
+// thread.h :
+//
+
+#if !defined(AGD_THREAD_H__8BEF8FA0_B86A_48C3_AA47_4BDCB70DE22D__INCLUDED_)
+#define AGD_THREAD_H__8BEF8FA0_B86A_48C3_AA47_4BDCB70DE22D__INCLUDED_
+
+#include <pthread.h>	// Compile and link with -pthread
+#include <vector>
+
+/////////////////////////////////////////////////////////////////////////////
+// thread.h - Declarations:
+
+class CThread
+{
+public:
+	typedef void* (*PFN_THREAD_ROUTINE) (void*);
+	typedef void (*PFN_CLEANUP_ROUTINE) (void*);
+
+private:
+	typedef struct _INTTP
+	{
+		CThread *pThis;
+		void *pParam;
+	}INTTP, *LPINTTP;
+	typedef const INTTP *LPCINTTP;
+
+public:
+	CThread(void);
+	virtual ~CThread(void);
+
+	int Create(PFN_THREAD_ROUTINE pFunc, void *pParam = NULL);
+	int Create(void *pParam = NULL);
+	
+	int Cancel(void);
+	int Join(void **ppRetval);
+	int Detach(void);
+	
+	int Signal(int nSig);
+	
+	pthread_t GetID(void) const {
+		return m_tID;
+	}
+
+protected:
+	int WaitSignal(int *pSignal);
+	int WaitSignalTimeout(unsigned long long nUs, int *pSignal);
+	bool PopSignal(int *pSignal);
+	virtual void* ThreadRoutine(void *pParam);
+
+private:
+	static void* _ThreadStart(void *pParam);
+
+private:
+	pthread_t m_tID;
+	pthread_attr_t m_tAttr;
+	pthread_cond_t m_tCond;
+	pthread_mutex_t m_tMtxCond;
+	std::vector<int> m_vSigs;
+	int m_nCondSig;
+	bool m_bSignaled;
+	bool m_bBlocked;
+	INTTP m_itp;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_THREAD_H__8BEF8FA0_B86A_48C3_AA47_4BDCB70DE22D__INCLUDED_)