Pārlūkot izejas kodu

Neuer SysInfo Process erste Version zur Ansicht.

Rind 5 gadi atpakaļ
vecāks
revīzija
699b1e1580

+ 2 - 2
Test/Test.pro.user

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 4.11.1, 2020-03-03T13:14:43. -->
+<!-- Written by QtCreator 4.11.1, 2020-03-17T16:28:46. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>
@@ -73,7 +73,7 @@
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{b0a415a1-ecbe-4123-8afc-05ffc0004131}</value>
    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
-   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">3</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
     <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/wrk/share/gfaipc/Test/Debug/Sitara_1</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">

+ 65 - 4
Test/main.cpp

@@ -31,6 +31,7 @@ static volatile bool g_fPause	= false;
 static volatile bool g_fHang	= false;
 static volatile bool g_fAlloc	= false;
 static volatile bool g_fFree	= false;
+static volatile bool g_fSysInfoRunning	= false;
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -145,9 +146,65 @@ static void _ProcessStateEvents(HAPPCTRL hAC, HAPPINFO hAI)
 				}
 			}
 			break;
-		case GFA_APPCTRL_APPID_REST:
+		case GFA_APPCTRL_APPID_SYSINFO:
+			if(!(g_fSysInfoRunning = (state == GIAS_Running)))
+			{
+				TRACE("SysInfo not running - System information not available!\n");
+			}
+			else
+			{
+				TRACE("SysInfo running - System information is available!\n");
+			}
+			break;
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void _ProcessSystemEvents(HAPPCTRL hAC, HAPPINFO hAI)
+{
+	int nIndex;
+	sysevt_t nEvent;
+	GFA_SYSINFO_DISK disk;
+	GFA_SYSINFO_PARTITION part;
+	
+	if(!g_fSysInfoRunning)
+		return;
+
+	while(g_fRun && (nEvent = ::GfaIpcAppCtrlGetNextSysEvt(hAI)))
+	{
+		switch(nEvent)
+		{
+		case GFA_APPCTRL_SYSEVENT_DISK_EVT:
+			while((nIndex = ::GfaIpcAppCtrlGetNextDiskRemoved(hAC, &disk)) >= 0)
+			{
+				TRACE("Disk [ID=%d] removed: %s [%s]\n", nIndex, disk.szDevNode, *disk.szName ? disk.szName : "Unnamed");
+			}
+			while((nIndex = ::GfaIpcAppCtrlGetNextDiskAdded(hAC, &disk)) >= 0)
+			{
+				TRACE("Disk [ID=%d] added:   %s [%s]\n", nIndex, disk.szDevNode, *disk.szName ? disk.szName : "Unnamed");
+			}
 			break;
-		case GFA_APPCTRL_APPID_MQTTCL:
+		case GFA_APPCTRL_SYSEVENT_PART_EVT:
+			while((nIndex = ::GfaIpcAppCtrlGetNextPartitionRemoved(hAC, &part)) >= 0)
+			{
+				TRACE("Partition [ID=%zd:%d] removed: %s [%s]\n", part.nDiskIdx, nIndex, part.szDevNode, *part.szFsLabel ? part.szFsLabel : "Unnamed");
+			}
+			while((nIndex = ::GfaIpcAppCtrlGetNextPartitionAdded(hAC, &part)) >= 0)
+			{
+				TRACE("Partition [ID=%zd:%d] added:   %s [%s]\n", part.nDiskIdx, nIndex, part.szDevNode, *part.szFsLabel ? part.szFsLabel : "Unnamed");
+			}
+			break;
+		case GFA_APPCTRL_SYSEVENT_MOUNT_EVT:
+			while((nIndex = ::GfaIpcAppCtrlGetNextMountRemoved(hAC, &part)) >= 0)
+			{
+				TRACE("Mountpoint [ID=%zd:%d] removed: %s\n", part.nDiskIdx, nIndex, part.szDevNode);
+			}
+			while((nIndex = ::GfaIpcAppCtrlGetNextMountAdded(hAC, &part)) >= 0)
+			{
+				TRACE("Mountpoint [ID=%zd:%d] added:   %s -> %s\n", part.nDiskIdx, nIndex, part.szDevNode, part.szMntPoint);
+			}
 			break;
 		}
 	}
@@ -178,7 +235,7 @@ static int _DoWork(void)
 
     if(g_fAlloc)
     {
-        TRACE("Wasting %d KiB of memory.\n", _MEM_WASTE_SIZE >> 10);
+        TRACE("Allocating %d KiB of memory.\n", _MEM_WASTE_SIZE >> 10);
         void *p = malloc(_MEM_WASTE_SIZE);
         g_vP.push_back(p);
         g_fAlloc = false;
@@ -260,7 +317,10 @@ int main(void)
 		::GfaIpcAppCtrlPresetDisplayName(hAC, GFA_APPCTRL_APPID_USER_01, "qmlApp");
 		::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing);
 
-		if(!::GfaIpcAppCtrlSubscribeStateEvents(hAC, _APPID_1 | _APPID_2 | GFA_APPCTRL_APPID_USER_01))
+		if(!::GfaIpcAppCtrlSubscribeStateEvents(hAC, _APPID_1 | _APPID_2 | GFA_APPCTRL_APPID_USER_01 | GFA_APPCTRL_APPID_SYSINFO))
+			break;
+
+		if(!::GfaIpcAppCtrlSubscribeSysEvents(hAC, GFA_APPCTRL_SYSEVENT_ALL_STG_DEV))
 			break;
 
 		TRACE("My Name:  %s\n", _APPNAME_0);
@@ -300,6 +360,7 @@ int main(void)
 		{
 			_ProcessCtrlMessages(hAC, hAI);
 			_ProcessStateEvents(hAC, hAI);
+			_ProcessSystemEvents(hAC, hAI);
 		}
 
 		/////////////////////////////////////////////////////////////////////

+ 1 - 1
Test/qmlApp/main.qml

@@ -1016,7 +1016,7 @@ Window {
         width: 120
         height: 460
 
-		property var appInfo: qGfaAppCtrl.appInfo[QGfaAppCtrl.APP_INDEX_SUMMARIST]
+		property var appInfo: qGfaAppCtrl.appInfo[QGfaAppCtrl.APP_INDEX_SYSINFO]
 
 		Rectangle {
 	        x: 0

+ 37 - 19
Test/qmlApp/qappctrl.cpp

@@ -34,7 +34,8 @@ QGfaAppInfo::QGfaAppInfo(int nIndex, QObject *pParent) :	QObject(pParent),
 															m_vmPeak(0),
 															m_vmSize(0),
 															m_vmHWM(0),
-															m_vmRSS(0)
+															m_vmRSS(0),
+															m_appSize(0)
 {
 	m_nAppID = 1ull << m_nIndex;
 	setStateText(m_state);
@@ -47,6 +48,13 @@ QGfaAppInfo::~QGfaAppInfo(void)
 
 /////////////////////////////////////////////////////////////////////////////
 
+appid_t QGfaAppInfo::appId(void) const
+{
+	return m_nAppID;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 int QGfaAppInfo::state(void) const
 {
 	return m_state;
@@ -177,6 +185,31 @@ quint32 QGfaAppInfo::vmRSS(void) const
 	return m_vmRSS;
 }
 
+quint32 QGfaAppInfo::appSize(void) const
+{
+	return m_appSize;
+}
+
+void QGfaAppInfo::setAppSize(quint32 size, GfaIpcAppStates state)
+{
+	if((state > GIAS_StateNotRunning) && (state < GIAS_Invalid))
+	{
+		if(m_appSize != size)
+		{
+			m_appSize = size;
+			emit appSizeChanged(m_appSize);
+		}
+	}
+	else
+	{
+		if(m_appSize != 0)
+		{
+			m_appSize = 0;
+			emit appSizeChanged(m_appSize);
+		}
+	}
+}
+
 void QGfaAppInfo::setAppMemInfo(LPCGFA_APPCTRL_APPMEM pam, GfaIpcAppStates state, bool bDoHeavyLoadUpdate)
 {
 	UNUSED(state);
@@ -568,24 +601,6 @@ void QGfaAppCtrl::timerEvent(QTimerEvent *event)
 			GFA_APPCTRL_APPTIMES at;
 			GFA_APPCTRL_APPMEM am;
 			char szDispName[128];
-#if 0
-		    ctrlmsg_t nCtrlMsg;
-
-			while((nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI)))
-			{
-				switch(nCtrlMsg)
-				{
-				case GFA_APPCTRL_CTRLMSG_STOP:
-					break;
-				case GFA_APPCTRL_CTRLMSG_PAUSE:
-					break;
-				case GFA_APPCTRL_CTRLMSG_RESUME:
-					break;
-				default:
-					break;
-				}
-			}
-#endif
 
 			while((nAppIdSrc = ::GfaIpcAppCtrlGetNextStateEvtSrc(hAI)))
 			{
@@ -640,6 +655,9 @@ void QGfaAppCtrl::timerEvent(QTimerEvent *event)
 
 					ai.setAppTimes(&at, state, bUpdate);
 				}
+
+				size_t nSize = ::GfaIpcAppCtrlGetAppSize(m_hAC, app);
+				ai.setAppSize((quint32)nSize, state);
 			}
 
 			if(bUpdate && !bDontSaveLast)

+ 10 - 1
Test/qmlApp/qappctrl.h

@@ -35,6 +35,7 @@ class QGfaAppInfo : public QObject
 	Q_PROPERTY(quint32 vmSize READ vmSize NOTIFY vmSizeChanged)
 	Q_PROPERTY(quint32 vmHWM READ vmHWM NOTIFY vmHWMChanged)
 	Q_PROPERTY(quint32 vmRSS READ vmRSS NOTIFY vmRSSChanged)
+	Q_PROPERTY(quint32 appSize READ appSize NOTIFY appSizeChanged)
 
 public:
 	Q_INVOKABLE bool pause(void);
@@ -61,10 +62,13 @@ signals:
 	void vmSizeChanged(quint32 val);
 	void vmHWMChanged(quint32 val);
 	void vmRSSChanged(quint32 val);
+	void appSizeChanged(quint32 val);
 
 public:
 	explicit QGfaAppInfo(int nIndex, QObject *pParent = NULL);
 	virtual ~QGfaAppInfo(void);
+	
+	appid_t appId(void) const;
 
 	int state(void) const;
 	bool setState(int val);
@@ -91,6 +95,9 @@ public:
 	quint32 vmHWM(void) const;
 	quint32 vmRSS(void) const;
 
+	quint32 appSize(void) const;
+	void setAppSize(quint32 size, GfaIpcAppStates state);
+
 	void setAppTimes(LPCGFA_APPCTRL_APPTIMES pat, GfaIpcAppStates state, bool bDoHeavyLoadUpdate = false);
 	void setAppMemInfo(LPCGFA_APPCTRL_APPMEM pam, GfaIpcAppStates state, bool bDoHeavyLoadUpdate = false);
 
@@ -117,6 +124,7 @@ private:
 	quint32 m_vmSize;
 	quint32 m_vmHWM;
 	quint32 m_vmRSS;
+	quint32 m_appSize;
 };
 
 /////////////////////////////////////////////////////////////////////////////
@@ -148,7 +156,8 @@ public:
 		APP_INDEX_DATALOGGER	= GAI_Datalogger,
 		APP_INDEX_SUMMARIST		= GAI_Summarist,
 		APP_INDEX_REST			= GAI_Rest,
-		APP_INDEX_MQTTCL		= GAI_Mqttcl
+		APP_INDEX_MQTTCL		= GAI_Mqttcl,
+		APP_INDEX_SYSINFO		= GAI_SysInfo
 	};
 	Q_ENUM(AppIndex)
 

+ 8 - 1
gfaipc.pro

@@ -14,6 +14,7 @@ SOURCES += \
     src/sema.cpp \
     src/shm.cpp \
     src/shmrot.cpp \
+    src/sysinfo.cpp \
     src/uuid.c
 
 HEADERS += \
@@ -25,6 +26,7 @@ HEADERS += \
     src/sema.h \
     src/shm.h \
     src/shmrot.h \
+    src/sysinfo.h \
     src/uuid.h
 
 ####################################################################################
@@ -32,6 +34,7 @@ HEADERS += \
 GFA_LIB_PATH = /usr/lib/gfa
 GFA_INC_PATH = /usr/include/gfa
 QMAKE_RPATHLINKDIR += $$GFA_LIB_PATH
+QMAKE_RPATHDIR += $$GFA_LIB_PATH
 QMAKE_DEL_DIR = rmdir --ignore-fail-on-non-empty
 QMAKE_LN_SHLIB = :
 
@@ -112,9 +115,13 @@ linux-buildroot-g++ {
 ####################################################################################
 
 deploylib.target = deploylib
-deploylib.commands = @echo TARGET="$(TARGET)" > deploytargets
+deploylib.commands = @echo BASENAME="$(QMAKE_TARGET)" > deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET="$(TARGET)" >> deploytargets
 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\\\"" >> deploytargets
 PRE_TARGETDEPS += deploylib
 QMAKE_EXTRA_TARGETS += deploylib
+
+# message($$basename(QMAKESPEC))

+ 35 - 7
gfaipc.pro.user

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 4.11.1, 2020-03-03T13:14:43. -->
+<!-- Written by QtCreator 4.11.1, 2020-03-17T16:28:46. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>
@@ -62,7 +62,7 @@
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Sitara 1</value>
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Sitara 1</value>
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{b0a415a1-ecbe-4123-8afc-05ffc0004131}</value>
-   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
@@ -95,7 +95,14 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
       <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
      </valuemap>
-     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.3">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Arguments">&quot;%{buildDir}&quot; &quot;%{sourceDir}&quot; &quot;%{CurrentBuild:Type}&quot; &quot;%{Qt:Mkspec}&quot;</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Command">/home/wrk/share/gfalibtools/create.sh</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">4</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
@@ -161,7 +168,14 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
       <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
      </valuemap>
-     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.3">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Arguments">&quot;%{buildDir}&quot; &quot;%{sourceDir}&quot; &quot;%{CurrentBuild:Type}&quot; &quot;%{Qt:Mkspec}&quot;</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Command">/home/wrk/share/gfalibtools/create.sh</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">4</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
@@ -378,7 +392,7 @@
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.57.gcc_64_kit</value>
-   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
@@ -411,7 +425,14 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
       <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
      </valuemap>
-     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.3">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Arguments">&quot;%{buildDir}&quot; &quot;%{sourceDir}&quot; &quot;%{CurrentBuild:Type}&quot; &quot;%{Qt:Mkspec}&quot;</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Command">/home/wrk/share/gfalibtools/create.sh</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">4</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
@@ -477,7 +498,14 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
       <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
      </valuemap>
-     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.3">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Arguments">&quot;%{buildDir}&quot; &quot;%{sourceDir}&quot; &quot;%{CurrentBuild:Type}&quot; &quot;%{Qt:Mkspec}&quot;</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.Command">/home/wrk/share/gfalibtools/create.sh</value>
+      <value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">4</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>

+ 32 - 0
gfasysinfo/gfasysinfo.pro

@@ -0,0 +1,32 @@
+TEMPLATE = app
+CONFIG += console
+CONFIG -= app_bundle
+CONFIG -= qt
+
+QMAKE_LIBDIR += $$[QT_SYSROOT]/usr/lib/gfa
+QMAKE_RPATHDIR += /usr/lib/gfa
+QMAKE_LIBS += -ludev
+
+CONFIG(debug, debug|release) {
+    QMAKE_CXXFLAGS -= -Os
+    QMAKE_CFLAGS -= -Os
+    QMAKE_CXXFLAGS += -D_DEBUG
+    QMAKE_CFLAGS += -D_DEBUG
+	QMAKE_LIBS += -lgfaipcd -lgfasitarautilsd -pthread
+}
+
+CONFIG(release, debug|release) {
+	QMAKE_LIBS += -lgfaipc -lgfasitarautils -pthread
+}
+
+linux-buildroot-g++ {
+    QMAKE_CXXFLAGS += -D_TARGET_BUILD
+    QMAKE_CFLAGS += -D_TARGET_BUILD
+    target.path += /opt/GfA/ipc/sysinfo
+	INSTALLS += target
+}
+
+SOURCES += \
+    src/main.cpp
+
+HEADERS +=

+ 500 - 0
gfasysinfo/gfasysinfo.pro.user

@@ -0,0 +1,500 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 4.11.1, 2020-03-17T16:28:46. -->
+<qtcreator>
+ <data>
+  <variable>EnvironmentId</variable>
+  <value type="QByteArray">{b9ed9a41-2c05-4012-9893-a7033fe3f9d2}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap"/>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Sitara 1</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Sitara 1</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{b0a415a1-ecbe-4123-8afc-05ffc0004131}</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/wrk/share/gfaipc/gfasysinfo/Debug/Sitara_1</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/wrk/share/gfaipc/gfasysinfo/Release/Sitara_1</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.CheckForFreeDiskSpaceStep</value>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
+      <value type="QString" key="RemoteLinux.CheckForFreeDiskSpaceStep.PathToCheck">/</value>
+      <value type="qlonglong" key="RemoteLinux.CheckForFreeDiskSpaceStep.RequiredSpace">5242880</value>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.KillAppStep</value>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.2">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinux.DirectUploadStep</value>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
+      <valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
+      <value type="bool" key="RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles">false</value>
+      <value type="bool" key="RemoteLinux.GenericDirectUploadStep.Incremental">true</value>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
+      <valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">3</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">DeployToGenericLinux</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.Events">
+     <value type="QString">cpu-cycles</value>
+    </valuelist>
+    <valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
+    <value type="int" key="Analyzer.Perf.Frequency">250</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
+     <value type="QString">-e</value>
+     <value type="QString">cpu-cycles</value>
+     <value type="QString">--call-graph</value>
+     <value type="QString">dwarf,4096</value>
+     <value type="QString">-F</value>
+     <value type="QString">250</value>
+    </valuelist>
+    <value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
+    <value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
+    <value type="int" key="Analyzer.Perf.StackSize">4096</value>
+    <value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
+    <value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
+    <value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
+    <value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
+    <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <value type="int" key="PE.EnvironmentAspect.Base">1</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">gfasysinfo (auf Sitara 1)</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinuxRunConfiguration:/home/wrk/share/gfaipc/gfasysinfo/gfasysinfo.pro</value>
+    <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/wrk/share/gfaipc/gfasysinfo/gfasysinfo.pro</value>
+    <value type="int" key="RemoteLinux.EnvironmentAspect.Version">1</value>
+    <value type="QString" key="RemoteLinux.RunConfig.AlternateRemoteExecutable"></value>
+    <value type="bool" key="RemoteLinux.RunConfig.UseAlternateRemoteExecutable">false</value>
+    <value type="QString" key="RunConfiguration.Arguments"></value>
+    <value type="bool" key="RunConfiguration.Arguments.multi">false</value>
+    <value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseX11Forwarding">false</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory"></value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
+    <value type="QString" key="RunConfiguration.X11Forwarding">:0</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.1</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.7.1 GCC 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.57.gcc_64_kit</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/wrk/share/gfaipc/gfasysinfo/Debug/Desktop_Qt_5_7_1_GCC_64bit</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/wrk/share/gfaipc/gfasysinfo/Release/Desktop_Qt_5_7_1_GCC_64bit</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.Events">
+     <value type="QString">cpu-cycles</value>
+    </valuelist>
+    <valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
+    <value type="int" key="Analyzer.Perf.Frequency">250</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
+     <value type="QString">-e</value>
+     <value type="QString">cpu-cycles</value>
+     <value type="QString">--call-graph</value>
+     <value type="QString">dwarf,4096</value>
+     <value type="QString">-F</value>
+     <value type="QString">250</value>
+    </valuelist>
+    <value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
+    <value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
+    <value type="int" key="Analyzer.Perf.StackSize">4096</value>
+    <value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
+    <value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
+    <value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
+    <value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
+    <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/wrk/share/gfaipc/gfasysinfo/gfasysinfo.pro</value>
+    <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/wrk/share/gfaipc/gfasysinfo/gfasysinfo.pro</value>
+    <value type="QString" key="RunConfiguration.Arguments"></value>
+    <value type="bool" key="RunConfiguration.Arguments.multi">false</value>
+    <value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory"></value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">2</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">22</value>
+ </data>
+ <data>
+  <variable>Version</variable>
+  <value type="int">22</value>
+ </data>
+</qtcreator>

+ 994 - 0
gfasysinfo/src/main.cpp

@@ -0,0 +1,994 @@
+#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 <usb.h>
+#include <gfa/gfasitarautils.h>
+//#include <gfa/gfaipc.h>
+#include "../../src/gfaipc.h"
+#include "../../src/ipcpriv.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 _APPID								GFA_APPCTRL_APPID_SYSINFO
+#define _APPNAME							"SysInfo"
+#define _CYCLE_INTV							500
+
+#define _STR_EQUALS(s, t)					(!!s && !strcmp(s, t))
+#define _NUM_TO_BIT(i)						(0x01 << (i))
+#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);
+
+/////////////////////////////////////////////////////////////////////////////
+
+// console=ttyO0,115200 root=/dev/mmcblk0p2 rw rootwait vram=50M cma=50M
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+ssize_t _LookupPartition(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode)
+{
+	ssize_t 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;
+}
+
+ssize_t _AddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_PARTITION &part, bool &bChange)
+{
+	ssize_t 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;
+}
+
+ssize_t _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nIndex, bool &bChange)
+{
+	bChange = false;
+	if(nIndex >= 0 && nIndex < (ssize_t)_countof(sdm.parts))
+	{
+		bChange = sdm.parts[nIndex].valid;
+		sdm.parts[nIndex].valid = false;
+	}
+	return nIndex;
+}
+
+ssize_t _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode, bool &bChange)
+{
+	ssize_t nIndex = _LookupPartition(sdm, pszDevNode);
+	return _RemovePartition(sdm, nIndex, bChange);
+}
+
+bool _PartitionSetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nPartIdx, ssize_t nDiskIdx, bool &bChange)
+{
+	bChange = false;
+	if(	(nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (ssize_t)_countof(sdm.disks)))
+	{
+		if(sdm.parts[nPartIdx].nDiskIdx != nDiskIdx)
+		{
+			sdm.parts[nPartIdx].nDiskIdx = nDiskIdx;
+			bChange = true;
+		}
+		return true;
+	}
+	return false;
+}
+
+ssize_t _PartitionGetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nPartIdx)
+{
+	if((nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)))
+		return sdm.parts[nPartIdx].nDiskIdx;
+	return -1;
+}
+
+void _ClearMapChanges(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	sdm.nPartChangeMask = 0;
+}
+
+bool _MapChanged(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	return !!sdm.nPartChangeMask;
+}
+
+unsigned int _SetPartitionChange(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nPartIdx)
+{
+	if((nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)))
+	{
+		unsigned int nChanged = _NUM_TO_BIT(nPartIdx);
+		sdm.nPartChangeMask |= nChanged;
+	}
+	return sdm.nPartChangeMask;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+ssize_t _LookupDisk(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode)
+{
+	ssize_t 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;
+}
+
+ssize_t _AddDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_DISK &disk, bool &bChange)
+{
+	ssize_t 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;
+}
+
+ssize_t _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nIndex, bool &bChange)
+{
+	bChange = false;
+	if(nIndex >= 0 && nIndex < (ssize_t)_countof(sdm.disks))
+	{
+		bChange = sdm.disks[nIndex].valid;
+		sdm.disks[nIndex].valid = false;
+	}
+	return nIndex;
+}
+
+ssize_t _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode, bool &bChange)
+{
+	ssize_t nIndex = _LookupDisk(sdm, pszDevNode);
+	return _RemoveDisk(sdm, nIndex, bChange);
+}
+
+unsigned int _DiskAddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nDiskIdx, ssize_t nPartIdx, bool &bChange)
+{
+	bChange = false;
+	if(	(nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (ssize_t)_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;
+}
+
+unsigned int _DiskRemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, ssize_t nDiskIdx, ssize_t nPartIdx, bool &bChange)
+{
+	bChange = false;
+	if(	(nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)) &&
+		(nDiskIdx >= 0) && (nDiskIdx < (ssize_t)_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 const char *g_pszStateNames[] =
+{
+	"Not running",
+	"Initializing",
+	"Running",
+	"Paused",
+	"Hanging",
+	"Terminating",
+	"Invalid"
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static volatile bool g_fRun		= false;
+static volatile bool g_fPause	= false;
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void _SigHandler(int sig)
+{
+	UNUSED(sig);
+	g_fPause = false;
+	g_fRun = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+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 _UpdatePartitionSizeInfo(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
+		{
+			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 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);
+			std::string val(szMount);
+			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)
+			{
+//				TRACE("Partition %s: mounted at %s\n", part.szDevNode, szMntPoint);
+				memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint));
+				_UpdatePartitionSizeInfo(mm, part);
+				_SetPartitionChange(sdm, i);
+				bChange = true;
+			}
+			else if(bMountedOld && !bMountedNew)
+			{
+//				TRACE("Partition %s: unmounted from %s\n", part.szDevNode, part.szMntPoint);
+				memset(part.szMntPoint, 0, sizeof(part.szMntPoint));
+				_UpdatePartitionSizeInfo(mm, part);
+				_SetPartitionChange(sdm, i);
+				bChange = true;
+			}
+			else if(bMountedOld && bMountedNew)
+			{
+				if(strcmp(part.szMntPoint, szMntPoint))
+				{
+//					TRACE("Partition %s: mount point changed: %s -> %s\n", part.szDevNode, part.szMntPoint, szMntPoint);
+					memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint));
+					_UpdatePartitionSizeInfo(mm, part);
+					_SetPartitionChange(sdm, i);
+					bChange = true;
+				}
+			}
+		}
+	}
+	return bChange;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
+{
+    ctrlmsg_t nCtrlMsg;
+
+	while(g_fRun && (nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI)))
+	{
+		switch(nCtrlMsg)
+		{
+		case GFA_APPCTRL_CTRLMSG_STOP:
+			g_fRun = false;
+			TRACE("Received Message: STOP!\n");
+			break;
+		case GFA_APPCTRL_CTRLMSG_PAUSE:
+			if(!g_fPause)
+			{
+				g_fPause = true;
+				::GfaIpcAppCtrlSetState(hAC, GIAS_Paused);
+				TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Paused]);
+			}
+			break;
+		case GFA_APPCTRL_CTRLMSG_RESUME:
+			if(g_fPause)
+			{
+				g_fPause = false;
+				::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
+				TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Running]);
+			}
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static const char* _GetFileSpec(const char *pszName)
+{
+	if(pszName && *pszName)
+	{
+		const char *pSlash =  strrchr(pszName, '/');
+		if(pSlash)
+			return  ++pSlash;
+		else
+			return pszName;
+	}
+
+	return NULL;
+}
+
+static bool _IsInternalEmmc(const char *pszDevNode)
+{
+	if(pszDevNode)
+	{
+		const char *pszDevName = _GetFileSpec(pszDevNode);
+		return _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART1) || _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART2);
+	}
+
+	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
+
+#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
+
+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 Byte", 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 _DumpStorageDeviceMap(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)
+		{
+			TRACE("Disk:\n");
+			TRACE("  |-Label:  %s\n", disk.szName);
+			TRACE("  |-Vendor: %s\n", *disk.szVendor ? disk.szVendor : "");
+			TRACE("  |-Node:   %s\n", disk.szDevNode);
+			TRACE("  |-Type:   %s\n", *disk.szBus ? disk.szBus : "mmc");
+
+			for(unsigned int j = 0; j < disk.nPartCount; ++j)
+			{
+				ssize_t 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)
+	{
+		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;
+		ssize_t nPartIdx = -1;
+		ssize_t nDiskIdx = -1;
+
+		const char *pszDevNode = udev_device_get_devnode(dev);
+
+		if(!pszDevNode)
+			return;
+		if(_IsInternalEmmc(pszDevNode))
+			return;	// skip internal emmc
+		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;
+
+			_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;
+
+			_UpdatePartitionSizeInfo(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);
+
+						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);
+
+						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* udev)
+{
+	struct udev_enumerate* enumerate = udev_enumerate_new(udev);
+	udev_enumerate_add_match_subsystem(enumerate, "block");
+	udev_enumerate_add_match_property(enumerate, "DEVTYPE", "partition");
+	udev_enumerate_scan_devices(enumerate);
+
+	struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
+	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(udev, pszPath);
+
+		_ProcessPartition(sdm, mm, dev);
+		udev_device_unref(dev);
+	}
+
+	udev_enumerate_unref(enumerate);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+{
+	UNUSED(argc);
+	UNUSED(argv);
+
+	HAPPCTRL hAC = NULL;
+	HAPPINFO hAI;
+	CCycleTimer ct(_CYCLE_INTV);
+	cy_time_t wStart, wEnd, wCur = 0;
+	struct udev* udev = 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;
+
+	pollfd pfd[2];
+	memset(&pfd, 0, sizeof(pfd));
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// 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 + '\'
+	sigaction(SIGTERM, &sa, NULL);	// handles normal termination
+	sigaction(SIGABRT, &sa, NULL);	// handles abnormal termination (i.e. abort())
+	sigaction(SIGINT, &sa, NULL);	// handles Ctrl + 'C'
+
+	// ignore signals
+	sa.sa_handler = SIG_IGN;
+    sigaction(SIGTSTP, &sa, NULL);	// ignores Ctrl + 'Z'
+    sigaction(SIGCHLD, &sa, NULL);	// ignores child process termination
+    sigaction(0, &sa, NULL);		// ignores shell termination
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// initialize
+
+	do
+	{
+		if(!(hAC = ::GfaIpcAppCtrlAcquire(_APPID, _APPNAME, _CYCLE_INTV * 1000, _CYCLE_INTV * 3000)))
+			break;
+
+		if(!::GfaIpcAppCtrlCreateSysInfo(hAC))
+			break;
+
+	    if(!(udev = udev_new()))
+	    {
+	        fprintf(stderr, "udev_new() failed\n");
+	        return 1;
+	    }
+
+		_UpdateMountMap(mm);
+		_EnumStorageDevices(sdm, mm, udev);
+		_DumpStorageDeviceMap(sdm);
+
+		if((pUdevMon = udev_monitor_new_from_netlink(udev, "udev")))
+		{
+			udev_monitor_filter_add_match_subsystem_devtype(pUdevMon, "block", "partition");
+			udev_monitor_enable_receiving(pUdevMon);
+
+			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();
+		::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing);
+
+//		if(!::GfaIpcAppCtrlSubscribeStateEvents(hAC, GFA_APPCTRL_APPID_USER_01))
+//			break;
+
+		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);
+
+		g_fRun = true;
+		::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
+		::GfaIpcAppCtrlUpdateStorageDeviceMap(hAC, sdm);
+	}
+	while(false);
+
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////
+	// run
+
+	while(g_fRun)
+	{
+		/////////////////////////////////////////////////////////////////////
+		// trigger cycle timer
+
+		ct.Trigger();
+
+		/////////////////////////////////////////////////////////////////////
+		// update app control info
+
+		if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, wCur)))
+		{
+			_ProcessCtrlMessages(hAC, hAI);
+//			_ProcessStateEvents(hAC, hAI);
+		}
+
+		/////////////////////////////////////////////////////////////////////
+		// if not paused, do work
+
+		if(!g_fPause && g_fRun)
+		{
+			wStart = ct.GetMicroTick();
+			::GfaIpcAppCtrlUpdateSysInfo(hAC);
+			wEnd = ct.GetMicroTick();
+			wCur = wEnd - wStart;
+
+			_ClearMapChanges(sdm);
+
+			while(poll(pfd, 2, _CYCLE_INTV / 5) > 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(_MapChanged(sdm))
+			{
+				::GfaIpcAppCtrlUpdateStorageDeviceMap(hAC, sdm);
+				_DumpStorageDeviceMap(sdm);
+			}
+		}
+
+		/////////////////////////////////////////////////////////////////////
+		// 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(udev)
+		udev_unref(udev);
+
+	if(hAC)
+	{
+		::GfaIpcAppCtrlSetState(hAC, GIAS_Terminating);
+		::GfaIpcAppCtrlReleaseSysInfo(hAC);
+		::GfaIpcAppCtrlRelease(hAC);
+	}
+
+	return 0;
+}

+ 0 - 101
main.c

@@ -1,101 +0,0 @@
-// main.c
-//
-#ifdef _WIN32
-#define _CRT_SECURE_NO_WARNINGS
-#include <windows.h>
-#include "../Win32/w32def.h"
-#define sleep(sec)				Sleep(sec * 1000)
-#endif	//	_WIN32
-
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ipc.h>
-#include <fcntl.h>
-#include "src/defines.h"
-#include "src/gfaipc.h"
-
-#define _UNUSED(v)				v = v
-
-typedef struct _TESTSTRUCT1
-{
-	int nCounter1;
-	int nCounter2;
-}TESTSTRUCT1, *LPTESTSTRUCT1;
-typedef const TESTSTRUCT1 *LPCTESTSTRUCT1;
-
-typedef struct _TESTSTRUCT2
-{
-	int nCounter1;
-	int nCounter2;
-}TESTSTRUCT2, *LPTESTSTRUCT2;
-typedef const TESTSTRUCT2 *LPCTESTSTRUCT2;
-
-#define _UUID_TESTSTRUCT1			"8c2e723a-5556-4d90-b5ed-bba8575b6f28"
-#define _UUID_TESTSTRUCT2			"8d961df7-96c4-45cd-8dd7-346227782ac0"
-
-
-int main(int argc, char *argv[])
-{
-	HSHM hShm1, hShm2;
-	LPTESTSTRUCT1 p1 = NULL;
-	LPTESTSTRUCT2 p2 = NULL;
-
-	_UNUSED(argc);
-	_UNUSED(argv);
-
-//	GfaIpcDumpSHMROT();
-
-	hShm1 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT1, sizeof(TESTSTRUCT1), 1, "Teststruct 1");
-	TRACE("Acquired SHM.\n");
-	p1 = (LPTESTSTRUCT1)GfaIpcAcquirePointer(hShm1);
-	TRACE("Acquired Pointer.\n");
-//	GfaIpcDumpSHMROT();
-	GfaIpcLockSHM(hShm1);
-	GfaIpcLockSHM(hShm1);
-	TRACE("Acquired Lock.\n");
-	sleep(10);
-//	hShm2 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT1, sizeof(TESTSTRUCT1), 1, "Teststruct 1");
-//	GfaIpcDumpSHMROT();
-
-#if 0
-	if(hShm1 && hShm2)
-	{
-		p1 = (LPTESTSTRUCT1)GfaIpcAcquirePointer(hShm1);
-		TRACE("Get pointer: %p\n", p1);
-		p2 = (LPTESTSTRUCT2)GfaIpcAcquirePointer(hShm2);
-		TRACE("Get pointer: %p\n", p2);
-
-		TRACE("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-		TRACE("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
-
-		GfaIpcLockSHM(hShm1);
-		p1->nCounter1++;
-		p1->nCounter2--;
-		GfaIpcUnlockSHM(hShm1);
-		TRACE("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-		TRACE("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
-
-		GfaIpcLockSHM(hShm2);
-		p2->nCounter1--;
-		p2->nCounter2++;
-		GfaIpcUnlockSHM(hShm2);
-		TRACE("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-		TRACE("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
-
-		GfaIpcReleasePointer(hShm1, p1);
-		GfaIpcReleasePointer(hShm2, p2);
-
-		GfaIpcReleaseSHM(hShm1);
-		GfaIpcReleaseSHM(hShm2);
-	}
-#endif
-
-	//uuid_t uuid;
-	//_uuid_parse("8c2e723a-5556-4d90-b5ed-bba8575b6f28", &uuid);
-
-	TRACE("Exit.\n");
-	return 0;
-}
-

+ 0 - 58
src/Makefile

@@ -1,58 +0,0 @@
-# (c) R. Russinger GfA 2017,... LGPL v3
-#
-# Makefile for GfA Libraries
-# call with make DEBUG=1 for Debug output
-# output is held in Release or Debug subfolder (folders are created automaticalle)
-# make Variable for foldernames := BINDIR
-#
-#
-DEBUG ?= 0
-        
-_LIBBASENAME=gfaipc
-LIBFILENAME=lib$(LIBBASENAME).so.1.1.0
-
-CFLAGS = -c -pthread -fPIC -Wall -Wno-unused -Wno-unused-label -Wformat -Wuninitialized -Wundef -Wcast-qual -Wwrite-strings -fabi-version=2 -fno-omit-frame-pointer
-CXXFLAGS = -c -pthread -fPIC -Wall -Wno-unused -Wno-unused-label -Wformat -Wuninitialized -Wundef -Wcast-qual -Wwrite-strings -std=c++11 -fabi-version=2 -fno-omit-frame-pointer
-LDFLAGS = -fPIC -shared -L. -lc -pthread
-
-ifeq ($(DEBUG), 1)
-	CFLAGS += -g
-	CXXFLAGS += -g
-	LDFLAGS += -g
-	LIBBASENAME = $(_LIBBASENAME)d
-	BINDIR=Debug
-else
-	CFLAGS += -O3
-	CXXFLAGS += -O3
-	LDFLAGS += -O3
-	LIBBASENAME = $(_LIBBASENAME)
-	BINDIR=Release
-endif
-
-$(shell mkdir -p $(BINDIR) > /dev/null)
-
-TARGET_LIB = $(LIBFILENAME) # target lib
-
-CSRCS = uuid.c  # source files
-CXXSRCS = sema.cpp mutex.cpp locmtx.cpp shm.cpp shmrot.cpp ipcshm.cpp # source files
-
-COBJS = $(CSRCS:%.c=$(BINDIR)/%.o)
-CXXOBJS = $(CXXSRCS:%.cpp=$(BINDIR)/%.o)
-
-.PHONY: all
-all: $(BINDIR)/${TARGET_LIB}
-
-$(BINDIR)/$(TARGET_LIB): $(COBJS) $(CXXOBJS)
-	$(CC) ${LDFLAGS} -o $@ $^
-	
-$(CSRCS:%.c=$(BINDIR)/%.d):$(BINDIR)/%.d:%.c
-	$(CC) $(CFLAGS) -MP -MT $(basename $@).o -MMD -MF $@ -o $(basename $@).o $<
-
-$(CXXSRCS:%.cpp=$(BINDIR)/%.dpp):$(BINDIR)/%.dpp:%.cpp
-	$(CXX) $(CXXFLAGS) -MP -MT $(basename $@).o -MMD -MF $@ -o $(basename $@).o $<
-
-include $(CSRCS:%.c=$(BINDIR)/%.d) $(CXXSRCS:%.cpp=$(BINDIR)/%.dpp)
-
-.PHONY: clean
-clean:
-	@rm -rf $(BINDIR)

+ 604 - 9
src/appctrl.cpp

@@ -5,6 +5,8 @@
 #include <linux/limits.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/stat.h>
+#include <libusb-1.0/libusb.h>
 #include "defines.h"
 #include "uuid.h"
 #include "appctrl.h"
@@ -26,6 +28,13 @@
 #define _IS_KNOWN_APP_INDEX(i)					(((i) >= 0) && ((i) < (int)_countof(g_pszKnownAppNames)))
 #define _MIN_CPU_UPDATE_INTERVAL_US				5000000
 
+#define _COPY_DISK(pd, ps)						memcpy(&pd, &ps, sizeof(pd))
+#define _DELETE_DISK(pd)						pd.valid = false
+#define _COPY_PARTITION(pd, ps)					memcpy(&pd, &ps, sizeof(pd))
+#define _DELETE_PARTITION(pd)					pd.valid = false
+#define _COPY_MOUNTPOINT(pd, ps)				memcpy(pd.szMntPoint, ps.szMntPoint, sizeof(pd.szMntPoint))
+#define _DELETE_MOUNTPOINT(pd)					memset(pd.szMntPoint, 0, sizeof(pd.szMntPoint))
+
 /////////////////////////////////////////////////////////////////////////////
 
 static const char *g_pszKnownAppNames[] =
@@ -34,7 +43,8 @@ static const char *g_pszKnownAppNames[] =
 	"Datalogger",
 	"Summarist",
 	"REST",
-	"MqttCl"
+	"MqttCl",
+	"GfaSysInfo"
 };
 
 /////////////////////////////////////////////////////////////////////////////
@@ -110,6 +120,16 @@ const char* CAppCtrl::GetAppPath(char *pszPath, size_t nCChPath, pid_t pid)
 
 /////////////////////////////////////////////////////////////////////////////
 
+size_t CAppCtrl::GetAppSize(const char *pszAppPath)
+{
+	struct stat st;
+	if(!::stat(pszAppPath, &st))
+		return st.st_size;
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 bool CAppCtrl::Create(appid_t nAppID, const char *pszDisplayName, clock64_t nCycleIntvUs, clock64_t nMaxHeartbeatDelay)
 {
 	if(m_hShm)
@@ -132,8 +152,9 @@ bool CAppCtrl::Create(appid_t nAppID, const char *pszDisplayName, clock64_t nCyc
 
 	if(!CAppCtrl::GetAppPath(szAppPath, sizeof(szAppPath), m_pid))
 		return false;
-	
-	m_procMem.Update();
+
+	if(!m_procMem.Update())
+		return false;
 
 	if((m_hShm = ::GfaIpcAcquireSHM(_UUID_SHM_APP_CTRL, sizeof(APP_CTRL), 1, _NAME_SHM_APP_CTRL)))
 	{
@@ -168,15 +189,15 @@ bool CAppCtrl::Create(appid_t nAppID, const char *pszDisplayName, clock64_t nCyc
 			{
 				memset(&proc, 0, sizeof(APP_CTRL_PROCESS));
 
-				proc.at.nTsStart	= time(NULL);
-				proc.pid			= m_pid;
-				proc.nAppID			= m_nAppID = nAppID;
+				proc.at.nTsStart			= time(NULL);
+				proc.pid					= m_pid;
+				proc.nAppID					= m_nAppID = nAppID;
 				proc.at.nCyclePresetUs		= nCycleIntvUs;
 				proc.at.nMaxHeartbeatDelay	= nMaxHeartbeatDelay;
-
 				if(pszDisplayName)
 					strcpy(proc.szDisplayName, pszDisplayName);
 				strcpy(proc.szAppPath, szAppPath);
+				proc.nAppFileSize = (uint32_t)GetAppSize(szAppPath);
 				return true;
 			}
 		}
@@ -248,6 +269,7 @@ LPAPP_CTRL_INFO CAppCtrl::AppInfoUpdate(clock64_t nCurWorkingTime)
 	{
 		struct timespec	tsCur;
 		APP_CTRL_PROCESS &proc	= m_pAppCtrl->proc[m_nSlotIdx];
+		APP_CTRL_SYSTEM &sys	= m_pAppCtrl->sys;
 		CAppCtrlShmLocker locker(*this);
 
 		m_ai.nStateEvtPending	= proc.nStateEvtPending;
@@ -255,6 +277,38 @@ LPAPP_CTRL_INFO CAppCtrl::AppInfoUpdate(clock64_t nCurWorkingTime)
 		proc.nStateEvtPending	= 0;
 		proc.nCtrlMsgPending	= 0;
 
+		if(sys.nPidInCharge)
+		{
+			if(proc.nSysEvtRegistered & GFA_APPCTRL_SYSEVENT_DISK_EVT)
+			{
+				if((proc.nDiskAdded = (proc.nDiskMask ^ sys.nDiskMask) & sys.nDiskMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_DISK_EVT;
+				if((proc.nDiskRemoved = (proc.nDiskMask ^ sys.nDiskMask) & ~sys.nDiskMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_DISK_EVT;
+			}
+
+			if(proc.nSysEvtRegistered & GFA_APPCTRL_SYSEVENT_PART_EVT)
+			{
+				if((proc.nPartAdded = (proc.nPartMask ^ sys.nPartMask) & sys.nPartMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_PART_EVT;
+				if((proc.nPartRemoved = (proc.nPartMask ^ sys.nPartMask) & ~sys.nPartMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_PART_EVT;
+			}
+
+			if(proc.nSysEvtRegistered & GFA_APPCTRL_SYSEVENT_MOUNT_EVT)
+			{
+				if((proc.nMountAdded = (proc.nMountMask ^ sys.nMountMask) & sys.nMountMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_MOUNT_EVT;
+				if((proc.nMountRemoved = (proc.nMountMask ^ sys.nMountMask) & ~sys.nMountMask))
+					m_ai.nSysEvtPending |= GFA_APPCTRL_SYSEVENT_MOUNT_EVT;
+			}
+
+
+			proc.nDiskMask = sys.nDiskMask;
+			proc.nPartMask = sys.nPartMask;
+			proc.nMountMask = sys.nMountMask;
+		}
+
 		::clock_gettime(CLOCK_MONOTONIC, &tsCur);
 		proc.nHeartbeatLastUs = proc.nHeartbeatCurUs;
 		proc.nHeartbeatCurUs = _TIMESPEC_2_US(tsCur);
@@ -309,7 +363,7 @@ LPAPP_CTRL_INFO CAppCtrl::AppInfoUpdate(clock64_t nCurWorkingTime)
 				if(proc.at.nCycleMaxUs < proc.at.nCycleLastUs)
 					proc.at.nCycleMaxUs = proc.at.nCycleLastUs;
 			}
-			
+
 			if(nCurWorkingTime)
 			{
 				proc.at.nWorkLastUs = nCurWorkingTime;
@@ -512,6 +566,25 @@ bool CAppCtrl::PresetDisplayName(appid_t nAppID, const char *pszName)
 
 /////////////////////////////////////////////////////////////////////////////
 
+size_t CAppCtrl::GetAppSize(appid_t nAppID)
+{
+	if(m_hShm)
+	{
+		CAppCtrlShmLocker locker(*this);
+		int nIndex = SlotIndexFromAppID(nAppID);
+
+		if(_IS_VALID_SLOT_INDEX(nIndex))
+		{
+			APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[nIndex];
+			return proc.nAppFileSize;
+		}
+	}
+
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 const char* CAppCtrl::GetKnownAppDisplayName(appid_t nAppID, char *pszName, size_t nCChName)
 {
 	if(pszName && nCChName)
@@ -567,6 +640,34 @@ bool CAppCtrl::UnsubscribeStateEvents(appid_t nAppMask)
 
 /////////////////////////////////////////////////////////////////////////////
 
+bool CAppCtrl::SubscribeSysEvents(sysevt_t nEvtMask)
+{
+	if(m_hShm)
+	{
+		CAppCtrlShmLocker locker(*this);
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		proc.nSysEvtRegistered |= nEvtMask;
+		return true;
+	}
+
+	return false;
+}
+
+bool CAppCtrl::UnsubscribeSysEvents(sysevt_t nEvtMask)
+{
+	if(m_hShm)
+	{
+		CAppCtrlShmLocker locker(*this);
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		proc.nSysEvtRegistered &= ~nEvtMask;
+		return true;
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 appid_t CAppCtrl::GetPendingStateEvents(void)
 {
 	appid_t nRet = 0;
@@ -621,6 +722,24 @@ ctrlmsg_t CAppCtrl::GetPendingCtrlMsg(void)
 
 /////////////////////////////////////////////////////////////////////////////
 
+sysevt_t CAppCtrl::GetPendingSysEvents(void)
+{
+	sysevt_t nRet = 0;
+
+	if(m_hShm)
+	{
+		CAppCtrlShmLocker locker(*this);
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		nRet = proc.nSysEvtPending;
+		if(nRet)
+			proc.nSysEvtPending = 0;
+	}
+
+	return nRet;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 clock64_t CAppCtrl::GetAppTimes(appid_t nAppID, LPGFA_APPCTRL_APPTIMES pat)
 {
 	clock64_t ret = -1;
@@ -667,6 +786,337 @@ bool CAppCtrl::GetAppMem(appid_t nAppID, LPGFA_APPCTRL_APPMEM pam)
 
 /////////////////////////////////////////////////////////////////////////////
 
+bool CAppCtrl::CreateSysInfo(void)
+{
+	APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+	CAppCtrlShmLocker locker(*this);
+
+	if(!sys.nPidInCharge)
+	{
+		memset(&sys, 0, sizeof(APP_CTRL_SYSTEM));
+		sys.nPidInCharge = m_pid;
+		return true;
+	}
+	else
+	{
+		pid_t pid = getpgid(sys.nPidInCharge);
+
+		if((pid < 0) && (errno == ESRCH))
+		{
+			memset(&sys, 0, sizeof(APP_CTRL_SYSTEM));
+			sys.nPidInCharge = m_pid;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool CAppCtrl::ReleaseSysInfo(void)
+{
+	APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+	CAppCtrlShmLocker locker(*this);
+
+	if(sys.nPidInCharge == m_pid)
+	{
+		memset(&sys, 0, sizeof(APP_CTRL_SYSTEM));
+		return true;
+	}
+
+	return false;
+}
+
+bool CAppCtrl::UpdateSysInfo(void)
+{
+	if(m_hShm)
+	{
+		struct timespec	tsCur;
+		::clock_gettime(CLOCK_MONOTONIC, &tsCur);
+		clock64_t t = _TIMESPEC_2_US(tsCur);
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+
+		CAppCtrlShmLocker locker(*this);
+
+		if(t < (sys.nClkLastUpdate + 1000000))
+			return true;
+
+		if(m_sysInfo.Update())
+		{
+			const SI_VAL &rMemTotal = m_sysInfo.MemTotal();
+			sys.nMemTotal = rMemTotal.valid ? (uint32_t)rMemTotal.cb : 0;
+
+			const SI_VAL &rMemUsed = m_sysInfo.MemUsed();
+			sys.nMemUsed = rMemUsed.valid ? (uint32_t)rMemUsed.cb : 0;
+
+			const SI_VAL &rMemFree = m_sysInfo.MemFree();
+			sys.nMemFree = rMemFree.valid ? (uint32_t)rMemFree.cb : 0;
+
+			const SI_VAL &rMemAvailable = m_sysInfo.MemAvailable();
+			sys.nMemAvailable = rMemAvailable.valid ? (uint32_t)rMemAvailable.cb : 0;
+
+			const SI_VAL &rMemBuffers = m_sysInfo.MemBuffers();
+			sys.nMemBuffers = rMemBuffers.valid ? (uint32_t)rMemBuffers.cb : 0;
+
+			const SI_VAL &rMemCached = m_sysInfo.MemCached();
+			sys.nMemCached = rMemCached.valid ? (uint32_t)rMemCached.cb : 0;
+
+			sys.nUpdateError = 0;
+			sys.nClkLastUpdate = t;
+		}
+		else
+		{
+			sys.nUpdateError = errno;
+			return false;
+		}
+	}
+
+	return false;
+}
+
+bool CAppCtrl::UpdateStorageDeviceMap(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+	unsigned int nPartChangeMask = sdm.nPartChangeMask;
+	CAppCtrlShmLocker locker(*this);
+
+	while(nPartChangeMask)
+	{
+		ssize_t nPartIdx = ffsll(nPartChangeMask) - 1;
+		uint32_t nPartBit = (0x01 << nPartIdx);
+		nPartChangeMask &= ~nPartBit;
+		
+		if((nPartIdx >= 0) && (nPartIdx < (ssize_t)_countof(sdm.parts)))
+		{
+			const GFA_SYSINFO_PARTITION &partNew = sdm.parts[nPartIdx];
+			GFA_SYSINFO_PARTITION &partShm = sys.sdm.parts[nPartIdx];
+			ssize_t nDiskIdx = partNew.nDiskIdx;
+			
+			if((nDiskIdx >= 0) && (nDiskIdx < (ssize_t)_countof(sdm.disks)))
+			{
+				const GFA_SYSINFO_DISK &diskNew = sdm.disks[nDiskIdx];
+				GFA_SYSINFO_DISK &diskShm = sys.sdm.disks[nDiskIdx];
+				uint32_t nDiskBit = (0x01 << nDiskIdx);
+
+				if(diskNew.valid && !diskShm.valid)
+				{
+					// disk add
+					TRACE("IPC: Disk add!\n");
+					_COPY_DISK(diskShm, diskNew);
+					sys.nDiskMask |= nDiskBit;
+				}
+				else if(!diskNew.valid && diskShm.valid)
+				{
+					// disk remove
+					TRACE("IPC: Disk remove!\n");
+					_DELETE_DISK(diskShm);
+					sys.nDiskMask &= ~nDiskBit;
+				}
+				else if(diskNew.valid && diskShm.valid)
+				{
+					// disk change
+					TRACE("IPC: Disk change!\n");
+					_COPY_DISK(diskShm, diskNew);
+					sys.nDiskMask |= nDiskBit;
+				}
+
+				if(partNew.valid && !partShm.valid)
+				{
+					// partition add
+					TRACE("IPC: Partition add!\n");
+					_COPY_PARTITION(partShm, partNew);
+					sys.nPartMask |= nPartBit;
+				}
+				else if(!partNew.valid && partShm.valid)
+				{
+					// partition remove
+					TRACE("IPC: Partition remove!\n");
+					_DELETE_PARTITION(partShm);
+					sys.nPartMask &= ~nPartBit;
+				}
+				else if(partNew.valid && partShm.valid)
+				{
+					// partition change
+					if(*partNew.szMntPoint && !*partShm.szMntPoint)
+					{
+						// mountpoint add
+						TRACE("IPC: Mountpoint add!\n");
+						_COPY_MOUNTPOINT(partShm, partNew);
+						sys.nMountMask |= nPartBit;
+					}
+					else if(!*partNew.szMntPoint && *partShm.szMntPoint)
+					{
+						// mountpoint remove
+						TRACE("IPC: Mountpoint remove!\n");
+						_DELETE_MOUNTPOINT(partShm);
+						sys.nMountMask &= ~nPartBit;
+					}
+					else if(*partNew.szMntPoint && *partShm.szMntPoint)
+					{
+						// mountpoint change
+						TRACE("IPC: Mountpoint change!\n");
+						_COPY_MOUNTPOINT(partShm, partNew);
+						sys.nMountMask |= nPartBit;
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+int CAppCtrl::GetNextDiskAdded(LPGFA_SYSINFO_DISK pDisk)
+{
+	if(pDisk)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nDiskAdded)
+		{
+			int nDiskIdx = ffsll(proc.nDiskAdded) - 1;
+			uint32_t m = 0x1ULL << nDiskIdx;
+			proc.nDiskAdded &= ~m;
+
+			if((nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sys.sdm.disks)))
+			{
+				memcpy(pDisk, &sys.sdm.disks[nDiskIdx], sizeof(GFA_SYSINFO_DISK));
+				return nDiskIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int CAppCtrl::GetNextDiskRemoved(LPGFA_SYSINFO_DISK pDisk)
+{
+	if(pDisk)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nDiskRemoved)
+		{
+			int nDiskIdx = ffsll(proc.nDiskRemoved) - 1;
+			uint32_t m = 0x1ULL << nDiskIdx;
+			proc.nDiskRemoved &= ~m;
+
+			if((nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sys.sdm.disks)))
+			{
+				memcpy(pDisk, &sys.sdm.disks[nDiskIdx], sizeof(GFA_SYSINFO_DISK));
+				return nDiskIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int CAppCtrl::GetNextPartitionAdded(LPGFA_SYSINFO_PARTITION pPart)
+{
+	if(pPart)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nPartAdded)
+		{
+			int nPartIdx = ffsll(proc.nPartAdded) - 1;
+			uint32_t m = 0x1ULL << nPartIdx;
+			proc.nPartAdded &= ~m;
+
+			if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sys.sdm.parts)))
+			{
+				memcpy(pPart, &sys.sdm.parts[nPartIdx], sizeof(GFA_SYSINFO_PARTITION));
+				return nPartIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int CAppCtrl::GetNextPartitionRemoved(LPGFA_SYSINFO_PARTITION pPart)
+{
+	if(pPart)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nPartRemoved)
+		{
+			int nPartIdx = ffsll(proc.nPartRemoved) - 1;
+			uint32_t m = 0x1ULL << nPartIdx;
+			proc.nPartRemoved &= ~m;
+
+			if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sys.sdm.parts)))
+			{
+				memcpy(pPart, &sys.sdm.parts[nPartIdx], sizeof(GFA_SYSINFO_PARTITION));
+				return nPartIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int CAppCtrl::GetNextMountAdded(LPGFA_SYSINFO_PARTITION pPart)
+{
+	if(pPart)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nMountAdded)
+		{
+			int nPartIdx = ffsll(proc.nMountAdded) - 1;
+			uint32_t m = 0x1ULL << nPartIdx;
+			proc.nMountAdded &= ~m;
+
+			if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sys.sdm.parts)))
+			{
+				memcpy(pPart, &sys.sdm.parts[nPartIdx], sizeof(GFA_SYSINFO_PARTITION));
+				return nPartIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int CAppCtrl::GetNextMountRemoved(LPGFA_SYSINFO_PARTITION pPart)
+{
+	if(pPart)
+	{
+		APP_CTRL_SYSTEM &sys = m_pAppCtrl->sys;
+		APP_CTRL_PROCESS &proc = m_pAppCtrl->proc[m_nSlotIdx];
+		CAppCtrlShmLocker locker(*this);
+
+		if(proc.nMountRemoved)
+		{
+			int nPartIdx = ffsll(proc.nMountRemoved) - 1;
+			uint32_t m = 0x1ULL << nPartIdx;
+			proc.nMountRemoved &= ~m;
+
+			if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sys.sdm.parts)))
+			{
+				memcpy(pPart, &sys.sdm.parts[nPartIdx], sizeof(GFA_SYSINFO_PARTITION));
+				return nPartIdx;
+			}
+		}
+	}
+
+	return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 int CAppCtrl::SlotIndexFromAppID(appid_t nAppID)
 {
 	if(nAppID == 0)
@@ -835,6 +1285,26 @@ bool GfaIpcAppCtrlUnsubscribeStateEvents(HAPPCTRL hAC, appid_t nAppMask)
 
 /////////////////////////////////////////////////////////////////////////////
 
+bool GfaIpcAppCtrlSubscribeSysEvents(HAPPCTRL hAC, sysevt_t nEvtMask)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->SubscribeSysEvents(nEvtMask);
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool GfaIpcAppCtrlUnsubscribeSysEvents(HAPPCTRL hAC, sysevt_t nEvtMask)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->UnsubscribeSysEvents(nEvtMask);
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 appid_t GfaIpcAppCtrlGetPendingStateEvents(HAPPCTRL hAC)
 {
 	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
@@ -883,6 +1353,112 @@ bool GfaIpcAppCtrlGetAppMem(HAPPCTRL hAC, appid_t nAppID, LPGFA_APPCTRL_APPMEM p
 	return false;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+
+size_t GfaIpcAppCtrlGetAppSize(HAPPCTRL hAC, appid_t nAppID)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetAppSize(nAppID);
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+sysevt_t GfaIpcAppCtrlGetPendingSysEvents(HAPPCTRL hAC)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetPendingSysEvents();
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// for the GfaSysInfo process only, will not be documented!
+
+extern "C" bool GfaIpcAppCtrlCreateSysInfo(HAPPCTRL hAC)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->CreateSysInfo();
+	return false;
+}
+
+extern "C" bool GfaIpcAppCtrlReleaseSysInfo(HAPPCTRL hAC)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->ReleaseSysInfo();
+	return false;
+}
+
+extern "C" bool GfaIpcAppCtrlUpdateSysInfo(HAPPCTRL hAC)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->UpdateSysInfo();
+	return false;
+}
+
+extern "C" bool GfaIpcAppCtrlUpdateStorageDeviceMap(HAPPCTRL hAC, const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->UpdateStorageDeviceMap(sdm);
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int GfaIpcAppCtrlGetNextDiskAdded(HAPPCTRL hAC, LPGFA_SYSINFO_DISK pDisk)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextDiskAdded(pDisk);
+	return -1;
+}
+
+int GfaIpcAppCtrlGetNextDiskRemoved(HAPPCTRL hAC, LPGFA_SYSINFO_DISK pDisk)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextDiskRemoved(pDisk);
+	return -1;
+}
+
+int GfaIpcAppCtrlGetNextPartitionAdded(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextPartitionAdded(pPart);
+	return -1;
+}
+
+int GfaIpcAppCtrlGetNextPartitionRemoved(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextPartitionRemoved(pPart);
+	return -1;
+}
+
+int GfaIpcAppCtrlGetNextMountAdded(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextMountAdded(pPart);
+	return -1;
+}
+
+int GfaIpcAppCtrlGetNextMountRemoved(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart)
+{
+	CAppCtrl *p = reinterpret_cast<CAppCtrl*>(hAC);
+	if(p)
+		return p->GetNextMountRemoved(pPart);
+	return -1;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -902,6 +1478,8 @@ ctrlmsg_t GfaIpcAppCtrlGetNextCtrlMsg(HAPPINFO hAI)
 	return 0;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+
 appid_t GfaIpcAppCtrlGetNextStateEvtSrc(HAPPINFO hAI)
 {
 	LPAPP_CTRL_INFO pai = reinterpret_cast<LPAPP_CTRL_INFO>(hAI);
@@ -909,10 +1487,27 @@ appid_t GfaIpcAppCtrlGetNextStateEvtSrc(HAPPINFO hAI)
 	if(pai && pai->nStateEvtPending)
 	{
 		int b = ffsll(pai->nStateEvtPending) - 1;
-		appid_t ret = ((ctrlmsg_t)0x1 << b);
+		appid_t ret = ((appid_t)0x1 << b);
 		pai->nStateEvtPending &= ~ret;
 		return ret;
 	}
 
 	return 0;
 }
+
+/////////////////////////////////////////////////////////////////////////////
+
+sysevt_t GfaIpcAppCtrlGetNextSysEvt(HAPPINFO hAI)
+{
+	LPAPP_CTRL_INFO pai = reinterpret_cast<LPAPP_CTRL_INFO>(hAI);
+
+	if(pai && pai->nSysEvtPending)
+	{
+		int b = ffsll(pai->nSysEvtPending) - 1;
+		sysevt_t ret = ((sysevt_t)0x1 << b);
+		pai->nSysEvtPending &= ~ret;
+		return ret;
+	}
+
+	return 0;
+}

+ 56 - 7
src/appctrl.h

@@ -7,8 +7,10 @@
 #include <stdbool.h>
 #include <time.h>
 #include <sys/times.h>
+#include "ipcpriv.h"
 #include "gfaipc.h"
 #include "procmem.h"
+#include "sysinfo.h"
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus
@@ -20,20 +22,21 @@
 
 #define _APP_CTRL_MAX_SLOTS								((int)(sizeof(appid_t) * 8))
 
-#define _UUID_SHM_APP_CTRL_V1							"c3316f45-bcd6-424f-83bd-7d37ef9c3c6e"
-#define _NAME_SHM_APP_CTRL_V1							"GfA IPC Application Control SHM v1"
+#define _UUID_SHM_APP_CTRL_V1							"c3316f45-bcd6-424f-83bd-7d37ef9c3c6f"
+#define _NAME_SHM_APP_CTRL_V1							"GfA IPC Application-Control SHM v1"
 
 #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
 {
 	appid_t			nStateEvtPending;
 	ctrlmsg_t		nCtrlMsgPending;
-//	GFA_APPCTRL_APPTIMES at;
-//	GFA_APPCTRL_APPMEM am;
+	sysevt_t		nSysEvtPending;
 }APP_CTRL_INFO, *LPAPP_CTRL_INFO;
 typedef const APP_CTRL_INFO *LPCAPP_CTRL_INFO;
 
@@ -41,7 +44,22 @@ typedef const APP_CTRL_INFO *LPCAPP_CTRL_INFO;
 
 typedef struct _APP_CTRL_SYSTEM
 {
-	time_t nTsStart;
+	pid_t nPidInCharge;
+	time_t nTsSystemStart;
+	clock64_t nClkLastUpdate;
+	int nUpdateError;
+	uint32_t nMemTotal;
+	uint32_t nMemUsed;
+	uint32_t nMemFree;
+	uint32_t nMemAvailable;
+	uint32_t nMemBuffers;
+	uint32_t nMemCached;
+	uint32_t nDiskMask;
+	uint32_t nPartMask;
+	uint32_t nMountMask;
+//	uint64_t nUsbDeviceMask;
+//	GFA_USB_DEVICE_INFO usb[_USB_DEVICE_LIST_MAX_ENTRIES];
+	GFA_SYSINFO_STORAGE_DEVICE_MAP sdm;
 }APP_CTRL_SYSTEM, *LPAPP_CTRL_SYSTEM;
 typedef const APP_CTRL_SYSTEM *LPCAPP_CTRL_SYSTEM;
 
@@ -49,17 +67,28 @@ typedef const APP_CTRL_SYSTEM *LPCAPP_CTRL_SYSTEM;
 
 typedef struct _APP_CTRL_PROCESS : public APP_CTRL_INFO
 {
-	GFA_APPCTRL_APPTIMES at;
-	GFA_APPCTRL_APPMEM am;
+	GFA_APPCTRL_APPTIMES	at;
+	GFA_APPCTRL_APPMEM		am;
 	clock64_t		nHeartbeatCurUs;
 	clock64_t		nHeartbeatLastUs;
 	appid_t			nAppID;
 	appid_t			nStateEvtSources;
 	appid_t			nHangingApps;
+	sysevt_t		nSysEvtRegistered;
+	uint32_t		nDiskMask;
+	uint32_t		nDiskAdded;
+	uint32_t		nDiskRemoved;
+	uint32_t		nPartMask;
+	uint32_t		nPartAdded;
+	uint32_t		nPartRemoved;
+	uint32_t		nMountMask;
+	uint32_t		nMountAdded;
+	uint32_t		nMountRemoved;
 	char			szDisplayName[_APP_CTRL_MAX_DISPLAY_NAME_LENGTH];
 	char			szAppPath[_APP_CTRL_MAX_APP_PATH_LENGTH];
 	GfaIpcAppStates state;
 	pid_t			pid;
+	uint32_t		nAppFileSize;
 }APP_CTRL_PROCESS, *LPAPP_CTRL_PROCESS;
 typedef const APP_CTRL_PROCESS *LPCAPP_CTRL_PROCESS;
 
@@ -94,6 +123,7 @@ public:
 	~CAppCtrl(void);
 	
 	static const char* GetAppPath(char *pszPath, size_t nCChPath, pid_t pid = 0);
+	static size_t GetAppSize(const char *pszAppPath);
 	static const char* GetStateText(GfaIpcAppStates state);
 
 
@@ -109,6 +139,7 @@ public:
 	
 	const char* GetDisplayName(appid_t nAppID, char *pszName, size_t nCChName);
 	bool PresetDisplayName(appid_t nAppID, const char *pszName);
+	size_t GetAppSize(appid_t nAppID);
 	
 	bool SubscribeStateEvents(appid_t nAppMask);
 	bool UnsubscribeStateEvents(appid_t nAppMask);
@@ -120,6 +151,23 @@ public:
 	clock64_t GetAppTimes(appid_t nAppID, LPGFA_APPCTRL_APPTIMES pat);
 	bool GetAppMem(appid_t nAppID, LPGFA_APPCTRL_APPMEM pam);
 
+	bool CreateSysInfo(void);
+	bool ReleaseSysInfo(void);
+	bool UpdateSysInfo(void);
+	bool UpdateStorageDeviceMap(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm);
+
+
+	bool SubscribeSysEvents(sysevt_t nEvtMask);
+	bool UnsubscribeSysEvents(sysevt_t nEvtMask);
+	sysevt_t GetPendingSysEvents(void);
+
+	int GetNextDiskAdded(LPGFA_SYSINFO_DISK pDisk);
+	int GetNextDiskRemoved(LPGFA_SYSINFO_DISK pDisk);
+	int GetNextPartitionAdded(LPGFA_SYSINFO_PARTITION pPart);
+	int GetNextPartitionRemoved(LPGFA_SYSINFO_PARTITION pPart);
+	int GetNextMountAdded(LPGFA_SYSINFO_PARTITION pPart);
+	int GetNextMountRemoved(LPGFA_SYSINFO_PARTITION pPart);
+
 private:
 	int SlotIndexFromAppID(appid_t nAppID);
 	void AppStateChanged(appid_t nAppID, GfaIpcAppStates oldState, GfaIpcAppStates newState);
@@ -140,6 +188,7 @@ private:
 	clock64_t m_nClockStartUs;
 	clock64_t m_nLastTimesCallUs;
 	CProcMem m_procMem;
+	CSysInfo m_sysInfo;
 };
 
 /////////////////////////////////////////////////////////////////////////////

+ 79 - 1
src/gfaipc.h

@@ -42,11 +42,13 @@ typedef void										*HAPPINFO;
 typedef unsigned long long							appid_t;
 typedef unsigned long long							ctrlmsg_t;
 typedef long long									clock64_t;
+typedef unsigned long long							sysevt_t;
 
 /////////////////////////////////////////////////////////////////////////////
 
 #define GFA_APPCTRL_MAKE_APPID(i)					(1ULL << (i))
 #define GFA_APPCTRL_MAKE_CTRLMSG(m)					(1ULL << (m))
+#define GFA_APPCTRL_MAKE_SYSEVENT(e)				(1ULL << (e))
 
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -59,6 +61,7 @@ typedef enum
 	GAI_Summarist,
 	GAI_Rest,
 	GAI_Mqttcl,
+	GAI_SysInfo
 }GfaAppIndex;
 
 #define GFA_APPCTRL_APPID_REMANENT					GFA_APPCTRL_MAKE_APPID(GAI_Remanent)
@@ -66,8 +69,14 @@ typedef enum
 #define GFA_APPCTRL_APPID_SUMMARIST					GFA_APPCTRL_MAKE_APPID(GAI_Summarist)
 #define GFA_APPCTRL_APPID_REST						GFA_APPCTRL_MAKE_APPID(GAI_Rest)
 #define GFA_APPCTRL_APPID_MQTTCL					GFA_APPCTRL_MAKE_APPID(GAI_Mqttcl)
+#define GFA_APPCTRL_APPID_SYSINFO					GFA_APPCTRL_MAKE_APPID(GAI_SysInfo)
 
-#define GFA_APPCTRL_APPID_ALL_GFA					(GFA_APPCTRL_APPID_REMANENT | GFA_APPCTRL_APPID_DATALOGGER | GFA_APPCTRL_APPID_SUMMARIST | GFA_APPCTRL_APPID_REST | GFA_APPCTRL_APPID_MQTTCL)
+#define GFA_APPCTRL_APPID_ALL_GFA					(GFA_APPCTRL_APPID_REMANENT |	\
+													GFA_APPCTRL_APPID_DATALOGGER |	\
+													GFA_APPCTRL_APPID_SUMMARIST |	\
+													GFA_APPCTRL_APPID_REST |		\
+													GFA_APPCTRL_APPID_MQTTCL |		\
+													GFA_APPCTRL_APPID_SYSINFO)
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -149,6 +158,14 @@ 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)
+
+/////////////////////////////////////////////////////////////////////////////
+
 typedef enum
 {
 	GIAS_StateNotRunning,
@@ -181,6 +198,8 @@ typedef struct _GFA_APPCTRL_APPTIMES
 }GFA_APPCTRL_APPTIMES, *LPGFA_APPCTRL_APPTIMES;
 typedef const GFA_APPCTRL_APPTIMES *LPCGFA_APPCTRL_APPTIMES;
 
+/////////////////////////////////////////////////////////////////////////////
+
 typedef struct _GFA_APPCTRL_APPMEM
 {
 	size_t vmPeak;
@@ -192,6 +211,52 @@ typedef const GFA_APPCTRL_APPMEM *LPCGFA_APPCTRL_APPMEM;
 
 /////////////////////////////////////////////////////////////////////////////
 
+#define GFA_APPCTRL_MAX_DISKS						8
+#define GFA_APPCTRL_MAX_PARTITIONS					16
+#define GFA_APPCTRL_MAX_DEV_NODE_LENGTH				128
+#define GFA_APPCTRL_MAX_MOUNT_POINT_LENGTH			256
+#define GFA_APPCTRL_MAX_BUS_NAME_LENGTH				32
+#define GFA_APPCTRL_MAX_FS_LABEL_LENGTH				32
+#define GFA_APPCTRL_MAX_FS_TYPE_LENGTH				32
+#define GFA_APPCTRL_MAX_FS_VERSION_LENGTH			32
+#define GFA_APPCTRL_MAX_DISK_NAME_LENGTH			64
+#define GFA_APPCTRL_MAX_DISK_VENDOR_NAME_LENGTH		64
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_SYSINFO_PARTITION
+{
+	bool valid;
+	ssize_t nDiskIdx;
+	size_t nKiBPartSize;
+	size_t nKiBSize;
+	size_t nKiBFree;
+	size_t nKiBUsed;
+	char szFsLabel[GFA_APPCTRL_MAX_FS_LABEL_LENGTH];
+	char szFsType[GFA_APPCTRL_MAX_FS_TYPE_LENGTH];
+	char szFsVersion[GFA_APPCTRL_MAX_FS_VERSION_LENGTH];
+	char szDevNode[GFA_APPCTRL_MAX_DEV_NODE_LENGTH];
+	char szMntPoint[GFA_APPCTRL_MAX_MOUNT_POINT_LENGTH];
+}GFA_SYSINFO_PARTITION, *LPGFA_SYSINFO_PARTITION;
+typedef const GFA_SYSINFO_PARTITION *LPCGFA_SYSINFO_PARTITION;
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_SYSINFO_DISK
+{
+	bool valid;
+	char szDevNode[GFA_APPCTRL_MAX_DEV_NODE_LENGTH];
+	char szName[GFA_APPCTRL_MAX_DISK_NAME_LENGTH];
+	char szVendor[GFA_APPCTRL_MAX_DISK_VENDOR_NAME_LENGTH];
+	char szBus[GFA_APPCTRL_MAX_BUS_NAME_LENGTH];
+	unsigned int nPartCount;
+	ssize_t aPartIdx[GFA_APPCTRL_MAX_PARTITIONS];
+}GFA_SYSINFO_DISK, *LPGFA_SYSINFO_DISK;
+typedef const GFA_SYSINFO_DISK *LPCGFA_SYSINFO_DISK;
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
 HAPPCTRL		GfaIpcAppCtrlAcquire			(appid_t nAppID, const char *pszDisplayName, clock64_t nCycleIntvUs, clock64_t nMaxHeartbeatDelayUs);
 void			GfaIpcAppCtrlRelease			(HAPPCTRL hAC);
 
@@ -213,11 +278,24 @@ ctrlmsg_t		GfaIpcAppCtrlGetPendingCtrlMsg	(HAPPCTRL hAC);
 
 clock64_t		GfaIpcAppCtrlGetAppTimes		(HAPPCTRL hAC, appid_t nAppID, LPGFA_APPCTRL_APPTIMES pat);
 bool			GfaIpcAppCtrlGetAppMem			(HAPPCTRL hAC, appid_t nAppID, LPGFA_APPCTRL_APPMEM pam);
+size_t			GfaIpcAppCtrlGetAppSize			(HAPPCTRL hAC, appid_t nAppID);
+
+bool			GfaIpcAppCtrlSubscribeSysEvents		(HAPPCTRL hAC, sysevt_t nEvtMask);
+bool			GfaIpcAppCtrlUnsubscribeSysEvents	(HAPPCTRL hAC, sysevt_t nEvtMask);
+sysevt_t		GfaIpcAppCtrlGetPendingSysEvents	(HAPPCTRL hAC);
+
+int				GfaIpcAppCtrlGetNextDiskAdded		(HAPPCTRL hAC, LPGFA_SYSINFO_DISK pDisk);
+int				GfaIpcAppCtrlGetNextDiskRemoved		(HAPPCTRL hAC, LPGFA_SYSINFO_DISK pDisk);
+int				GfaIpcAppCtrlGetNextPartitionAdded	(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart);
+int				GfaIpcAppCtrlGetNextPartitionRemoved(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart);
+int				GfaIpcAppCtrlGetNextMountAdded		(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart);
+int				GfaIpcAppCtrlGetNextMountRemoved	(HAPPCTRL hAC, LPGFA_SYSINFO_PARTITION pPart);
 
 /////////////////////////////////////////////////////////////////////////////
 
 ctrlmsg_t		GfaIpcAppCtrlGetNextCtrlMsg		(HAPPINFO hAI);
 appid_t			GfaIpcAppCtrlGetNextStateEvtSrc	(HAPPINFO hAI);
+sysevt_t		GfaIpcAppCtrlGetNextSysEvt		(HAPPINFO hAI);
 
 const char*		GfaIpcAppCtrlGetStateText		(GfaIpcAppStates state);
 

+ 37 - 0
src/ipcpriv.h

@@ -0,0 +1,37 @@
+// ipcpriv.h :
+//
+
+#if !defined(AGD_IPCPRIV_H__BAF80510_9A96_4947_B481_D78F3AB2475B__INCLUDED_)
+#define AGD_IPCPRIV_H__BAF80510_9A96_4947_B481_D78F3AB2475B__INCLUDED_
+
+#include "gfaipc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif	//	__cplusplus
+
+/////////////////////////////////////////////////////////////////////////////
+// ipcpriv.h - Declarations:
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_SYSINFO_STORAGE_DEVICE_MAP
+{
+	unsigned int nPartChangeMask;
+	GFA_SYSINFO_DISK disks[GFA_APPCTRL_MAX_DISKS];
+	GFA_SYSINFO_PARTITION parts[GFA_APPCTRL_MAX_PARTITIONS];
+}GFA_SYSINFO_STORAGE_DEVICE_MAP, *LPGFA_SYSINFO_STORAGE_DEVICE_MAP;
+typedef const GFA_SYSINFO_STORAGE_DEVICE_MAP *LPCGFA_SYSINFO_STORAGE_DEVICE_MAP;
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool GfaIpcAppCtrlCreateSysInfo(HAPPCTRL hAC);
+bool GfaIpcAppCtrlReleaseSysInfo(HAPPCTRL hAC);
+bool GfaIpcAppCtrlUpdateSysInfo(HAPPCTRL hAC);
+bool GfaIpcAppCtrlUpdateStorageDeviceMap(HAPPCTRL hAC, const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm);
+
+/////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+}
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_IPCPRIV_H__BAF80510_9A96_4947_B481_D78F3AB2475B__INCLUDED_)

+ 62 - 3
src/procmem.cpp

@@ -17,7 +17,23 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-#define _STATUS_BUFFER_SIZE				1024
+#define _STATUS_BUFFER_SIZE				2048
+
+#define _FLAG_VM_PEAK					0x0001
+#define _FLAG_VM_SIZE					0x0002
+#define _FLAG_VM_HWM					0x0004
+#define _FLAG_VM_RSS					0x0008
+#define _FLAG_VM_LCK					0x0010
+#define _FLAG_VM_PIN					0x0020
+#define _FLAG_VM_DATA					0x0040
+#define _FLAG_VM_STK					0x0080
+#define _FLAG_VM_EXE					0x0100
+#define _FLAG_VM_LIB					0x0200
+#define _FLAG_VM_PTE					0x0400
+#define _FLAG_VM_PMD					0x0800
+#define _FLAG_VM_SWAP					0x1000
+
+#define _FLAG_VM_REDUCED				(_FLAG_VM_PEAK | _FLAG_VM_SIZE | _FLAG_VM_HWM | _FLAG_VM_RSS)
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -52,7 +68,7 @@ static char* _GetNextLine(char *psz, const char **ppszKey, const char **ppszValu
 				*ppszValue = psz;
 		}
 		
-		return *pszRet ? pszRet : NULL;
+		return (pszRet && *pszRet) ? pszRet : NULL;
 	}
 
 	return NULL;
@@ -92,17 +108,18 @@ CProcMem::~CProcMem(void)
 
 bool CProcMem::Update(void)
 {
-	char szStatus[_STATUS_BUFFER_SIZE];
 	int fd = open(m_szStatusFilePath, O_RDONLY, 0);
 
 	if(fd >= 0)
 	{
+		char szStatus[_STATUS_BUFFER_SIZE];
 	    int nRead = read(fd, szStatus, sizeof(szStatus) - 1);
 		close(fd);
 		
 		if(nRead > 0)
 		{
 			szStatus[nRead] = '\0';
+			unsigned int dwFound = 0;
 			char *pszStat = szStatus;
 			const char *pszKey, *pszValue;
 			
@@ -113,34 +130,76 @@ bool CProcMem::Update(void)
 				if(pszKey && *pszKey && pszValue && *pszValue)
 				{
 					if(!strcmp(pszKey, "VmPeak"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmPeak);
+						dwFound |= _FLAG_VM_PEAK;
+					}
 					else if(!strcmp(pszKey, "VmSize"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmSize);
+						dwFound |= _FLAG_VM_SIZE;
+					}
 					else if(!strcmp(pszKey, "VmHWM"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmHWM);
+						dwFound |= _FLAG_VM_HWM;
+					}
 					else if(!strcmp(pszKey, "VmRSS"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmRSS);
+						dwFound |= _FLAG_VM_RSS;
+					}
 #if _EXTENDED_VM_VALUES
 					else if(!strcmp(pszKey, "VmLck"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmLck);
+						dwFound |= _FLAG_VM_LCK;
+					}
 					else if(!strcmp(pszKey, "VmPin"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmPin);
+						dwFound |= _FLAG_VM_PIN;
+					}
 					else if(!strcmp(pszKey, "VmData"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmData);
+						dwFound |= _FLAG_VM_DATA;
+					}
 					else if(!strcmp(pszKey, "VmStk"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmStk);
+						dwFound |= _FLAG_VM_STK;
+					}
 					else if(!strcmp(pszKey, "VmExe"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmExe);
+						dwFound |= _FLAG_VM_EXE;
+					}
 					else if(!strcmp(pszKey, "VmLib"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmLib);
+						dwFound |= _FLAG_VM_LIB;
+					}
 					else if(!strcmp(pszKey, "VmPTE"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmPTE);
+						dwFound |= _FLAG_VM_PTE;
+					}
 					else if(!strcmp(pszKey, "VmPMD"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmPMD);
+						dwFound |= _FLAG_VM_PMD;
+					}
 					else if(!strcmp(pszKey, "VmSwap"))
+					{
 						_GetVmValue(pszValue, m_vmMap.VmSwap);
+						dwFound |= _FLAG_VM_SWAP;
+					}
 #endif // _EXTENDED_VM_VALUES
 				}
+
+				if((dwFound & _FLAG_VM_REDUCED) == _FLAG_VM_REDUCED)
+					break;
 			}
 			while(pszStat);
 

+ 162 - 0
src/sysinfo.cpp

@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include "defines.h"
+#include "sysinfo.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// https://linuxwiki.de/proc
+// https://linuxwiki.de/proc/pid
+// http://man7.org/linux/man-pages/man5/proc.5.html
+// https://linuxwiki.de/proc/meminfo
+//
+
+#define _BUFFER_SIZE						2048
+#define _MEM_INFO_PATH						"/proc/meminfo"
+
+#define _FLAG_MEM_TOTAL						0x0001
+#define _FLAG_MEM_FREE						0x0002
+#define _FLAG_MEM_AVAIL						0x0004
+#define _FLAG_MEM_BUFFERS					0x0008
+#define _FLAG_MEM_CACHED					0x0010
+
+#define _FLAG_ALL_MEM						(_FLAG_MEM_TOTAL | _FLAG_MEM_FREE | _FLAG_MEM_AVAIL | _FLAG_MEM_BUFFERS | _FLAG_MEM_CACHED)
+
+/////////////////////////////////////////////////////////////////////////////
+
+static char* _GetNextLine(char *psz, const char **ppszKey, const char **ppszValue)
+{
+	*ppszKey = *ppszValue = NULL;
+	
+	if(psz && *psz)
+	{
+		while(isspace(*psz))
+			++psz;
+
+		char *pszRet = NULL;
+		char *nl = strchr(psz, '\n');
+
+		if(nl)
+		{
+			*nl = '\0';
+			pszRet = ++nl;
+		}
+
+		char *cl = strchr(psz, ':');
+
+		if(cl)
+		{
+			*ppszKey = psz;
+			*cl = '\0';
+			psz = ++cl;
+			while(isspace(*psz))
+				++psz;
+			if(*psz)
+				*ppszValue = psz;
+		}
+		
+		return (pszRet && *pszRet) ? pszRet : NULL;
+	}
+
+	return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void _GetSiValueKB(const char *pszValue, SI_VAL &sv)
+{
+	char szUnit[3];
+	size_t val;
+	int n = sscanf(pszValue, "%zu %2s", &val, szUnit);
+
+	if((n == 2) && !strcmp(szUnit, "kB"))
+	{
+		sv.cb = val;
+		sv.valid = true;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+CSysInfo::CSysInfo(void)
+{
+	memset(&m_memInfo, 0, sizeof(m_memInfo));
+}
+
+CSysInfo::~CSysInfo(void)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool CSysInfo::Update(void)
+{
+	int fd;
+
+	if((fd = open(_MEM_INFO_PATH, O_RDONLY, 0)) >= 0)
+	{
+		char szBuf[_BUFFER_SIZE];
+	    int nRead = read(fd, szBuf, sizeof(szBuf) - 1);
+		close(fd);
+
+		if(nRead > 0)
+		{
+			szBuf[nRead] = '\0';
+			unsigned int dwFound = 0;
+			char *pszLine = szBuf;
+			const char *pszKey, *pszValue;
+			
+			do
+			{
+				pszLine = _GetNextLine(pszLine, &pszKey, &pszValue);
+				
+				if(pszKey && *pszKey && pszValue && *pszValue)
+				{
+					if(!strcmp(pszKey, "MemTotal"))
+					{
+						_GetSiValueKB(pszValue, m_memInfo.MemTotal);
+						dwFound |= _FLAG_MEM_TOTAL;
+					}
+					else if(!strcmp(pszKey, "MemFree"))
+					{
+						_GetSiValueKB(pszValue, m_memInfo.MemFree);
+						dwFound |= _FLAG_MEM_FREE;
+					}
+					else if(!strcmp(pszKey, "MemAvailable"))
+					{
+						_GetSiValueKB(pszValue, m_memInfo.MemAvailable);
+						dwFound |= _FLAG_MEM_AVAIL;
+					}
+					else if(!strcmp(pszKey, "Buffers"))
+					{
+						_GetSiValueKB(pszValue, m_memInfo.Buffers);
+						dwFound |= _FLAG_MEM_BUFFERS;
+					}
+					else if(!strcmp(pszKey, "Cached"))
+					{
+						_GetSiValueKB(pszValue, m_memInfo.Cached);
+						dwFound |= _FLAG_MEM_CACHED;
+					}
+				}
+				
+				if((dwFound & _FLAG_ALL_MEM) == _FLAG_ALL_MEM)
+				{
+					if((m_memInfo.MemUsed.valid = m_memInfo.MemTotal.valid && m_memInfo.MemFree.valid))
+						m_memInfo.MemUsed.cb = m_memInfo.MemTotal.cb - m_memInfo.MemFree.cb;
+					break;
+				}
+			}
+			while(pszLine);
+
+			return true;
+		}
+	}
+
+	return false;
+}

+ 72 - 0
src/sysinfo.h

@@ -0,0 +1,72 @@
+// sysinfo.h :
+//
+
+#if !defined(AGD_SYSINFO_H__2BA1C607_39A9_4627_8B42_DD0B57F6106D__INCLUDED_)
+#define AGD_SYSINFO_H__2BA1C607_39A9_4627_8B42_DD0B57F6106D__INCLUDED_
+
+#ifdef __cplusplus
+
+/////////////////////////////////////////////////////////////////////////////
+// sysinfo.h - Declarations:
+
+typedef struct _SI_VAL
+{
+	size_t cb;
+	bool valid;
+}SI_VAL, *LPSI_VAL;
+typedef const SI_VAL *LPCSI_VAL;
+
+typedef struct _MEM_INFO
+{
+	SI_VAL MemTotal;
+	SI_VAL MemUsed;
+	SI_VAL MemFree;
+	SI_VAL MemAvailable;
+	SI_VAL Buffers;
+	SI_VAL Cached;
+}MEM_INFO, *LPMEM_INFO;
+typedef const MEM_INFO *LPCMEM_INFO;
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CSysInfo
+{
+public:
+	CSysInfo(void);
+	~CSysInfo(void);
+	
+	bool Update(void);
+
+	const SI_VAL& MemTotal(void) const {
+		return m_memInfo.MemTotal;
+	}
+
+	const SI_VAL& MemUsed(void) const {
+		return m_memInfo.MemUsed;
+	}
+
+	const SI_VAL& MemFree(void) const {
+		return m_memInfo.MemFree;
+	}
+
+	const SI_VAL& MemAvailable(void) const {
+		return m_memInfo.MemAvailable;
+	}
+
+	const SI_VAL& MemBuffers(void) const {
+		return m_memInfo.Buffers;
+	}
+
+	const SI_VAL& MemCached(void) const {
+		return m_memInfo.Cached;
+	}
+
+private:
+
+private:
+	MEM_INFO m_memInfo;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_SYSINFO_H__2BA1C607_39A9_4627_8B42_DD0B57F6106D__INCLUDED_)