#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stgdevinfo.h" #include "mysqlinfo.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 ///////////////////////////////////////////////////////////////////////////// static volatile bool g_fRun = false; static volatile bool g_fPause = false; static volatile bool g_fZombie = false; ///////////////////////////////////////////////////////////////////////////// static const char *g_pszStateNames[] = { "Not running", "Initializing", "Running", "Paused", "Hanging", "Terminating", "Invalid" }; ///////////////////////////////////////////////////////////////////////////// #if 0 static long long _NumberFromString(const char *pszString, int base = 10, bool *pbErr = NULL); static const char* _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, char *pszValue, size_t nCChValue, bool bTruncate = false); static long long _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, int base = 10, bool *pbErr = NULL); ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// static int _LookupPartition(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode) { int nIndex = -1; if(pszDevNode && *pszDevNode) { for(size_t i = 0; i < _countof(sdm.parts); ++i) { if(sdm.parts[i].valid && !strcmp(sdm.parts[i].szDevNode, pszDevNode)) { nIndex = i; // found partition break; } } } return nIndex; } static int _AddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_PARTITION &part, bool &bChange) { int nIndex = -1; bChange = false; if((nIndex = _LookupPartition(sdm, part.szDevNode)) >= 0) return nIndex; // partition already exists for(size_t i = 0; i < _countof(sdm.parts); ++i) { if(!sdm.parts[i].valid) { memcpy(&sdm.parts[i], &part, sizeof(GFA_SYSINFO_PARTITION)); sdm.parts[i].valid = true; bChange = true; nIndex = i; break; } } return nIndex; } static int _RemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, bool &bChange) { bChange = false; if(nIndex >= 0 && nIndex < (int)_countof(sdm.parts)) { bChange = sdm.parts[nIndex].valid; sdm.parts[nIndex].valid = false; } return nIndex; } static bool _PartitionSetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx, int nDiskIdx, bool &bChange) { bChange = false; if( (nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) && (nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks))) { if(sdm.parts[nPartIdx].nDiskIdx != nDiskIdx) { sdm.parts[nPartIdx].nDiskIdx = nDiskIdx; bChange = true; } return true; } return false; } static int _PartitionGetDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx) { if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts))) return sdm.parts[nPartIdx].nDiskIdx; return -1; } static void _ClearMapChanges(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm) { sdm.nPartChangeMask = 0; } static bool _DeviceMapChanged(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm) { return !!sdm.nPartChangeMask; } static unsigned int _SetPartitionChange(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nPartIdx) { if((nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts))) { unsigned int nChanged = (0x01 << nPartIdx); sdm.nPartChangeMask |= nChanged; } return sdm.nPartChangeMask; } ///////////////////////////////////////////////////////////////////////////// static int _LookupDisk(const GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const char *pszDevNode) { int nIndex = -1; if(pszDevNode && *pszDevNode) { for(size_t i = 0; i < _countof(sdm.disks); ++i) { if(sdm.disks[i].valid && !strcmp(sdm.disks[i].szDevNode, pszDevNode)) { nIndex = i; // found partition break; } } } return nIndex; } static int _AddDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, const GFA_SYSINFO_DISK &disk, bool &bChange) { int nIndex = -1; bChange = false; if((nIndex = _LookupDisk(sdm, disk.szDevNode)) >= 0) return nIndex; // partition already exists for(size_t i = 0; i < _countof(sdm.disks); ++i) { if(!sdm.disks[i].valid) { memcpy(&sdm.disks[i], &disk, sizeof(GFA_SYSINFO_DISK)); sdm.disks[i].valid = true; bChange = true; nIndex = i; break; } } return nIndex; } static int _RemoveDisk(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nIndex, bool &bChange) { bChange = false; if(nIndex >= 0 && nIndex < (int)_countof(sdm.disks)) { bChange = sdm.disks[nIndex].valid; sdm.disks[nIndex].valid = false; } return nIndex; } static unsigned int _DiskAddPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nDiskIdx, int nPartIdx, bool &bChange) { bChange = false; if( (nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) && (nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks))) { GFA_SYSINFO_DISK &disk = sdm.disks[nDiskIdx]; for(unsigned int i = 0; i < disk.nPartCount; ++i) { if(disk.aPartIdx[i] == nPartIdx) return disk.nPartCount; } if(disk.nPartCount < _countof(disk.aPartIdx)) { disk.aPartIdx[disk.nPartCount++] = nPartIdx; bChange = true; return disk.nPartCount; } } return 0; } static unsigned int _DiskRemovePartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, int nDiskIdx, int nPartIdx, bool &bChange) { bChange = false; if( (nPartIdx >= 0) && (nPartIdx < (int)_countof(sdm.parts)) && (nDiskIdx >= 0) && (nDiskIdx < (int)_countof(sdm.disks))) { unsigned int i, j; GFA_SYSINFO_DISK &disk = sdm.disks[nDiskIdx]; for(i = 0; i < disk.nPartCount; ++i) { if(disk.aPartIdx[i] == nPartIdx) break; } if(i < disk.nPartCount) { for(j = i + 1; j < disk.nPartCount; ++i, ++j) { disk.aPartIdx[i] = disk.aPartIdx[j]; } bChange = true; return --disk.nPartCount; } return disk.nPartCount; } return 0; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// static bool _LookupMountPoint(MountMap &mm, const char *pszNode, char *pszMntPoint, size_t nCChMntPoint) { MountMap::const_iterator it = mm.find(pszNode); if(it == mm.end()) return false; const std::string &s = it->second; if(s.length() < nCChMntPoint) { strcpy(pszMntPoint, s.c_str()); return true; } return false; } static bool _UpdatePartitionFsInfo(MountMap &mm, GFA_SYSINFO_PARTITION &part) { if(_LookupMountPoint(mm, part.szDevNode, part.szMntPoint, sizeof(part.szMntPoint))) { struct statvfs stvs; if(!statvfs(part.szMntPoint, &stvs)) { part.nKiBSize = stvs.f_bsize * stvs.f_blocks / 1024; part.nKiBFree = stvs.f_bsize * stvs.f_bfree / 1024; part.nKiBUsed = part.nKiBSize - part.nKiBFree; } else { TRACE("statvfs failed on \"%s\": %s\n", part.szMntPoint, strerror(errno)); memset(part.szMntPoint, 0, sizeof(part.szMntPoint)); part.nKiBSize = 0; part.nKiBFree = 0; part.nKiBUsed = 0; } return true; } else { memset(part.szMntPoint, 0, sizeof(part.szMntPoint)); part.nKiBSize = 0; part.nKiBFree = 0; part.nKiBUsed = 0; } return false; } ///////////////////////////////////////////////////////////////////////////// static std::string _StrReplace(std::string &s, const char *pszFind, const char *pszRepl) { std::string r = s; if(pszFind && *pszFind && pszRepl) { size_t nFind, nLen = strlen(pszFind); while((nFind = r.find(pszFind)) != std::string::npos) { r = r.replace(nFind, nLen, pszRepl); } } return r; } static std::string _UnescapeMountpointString(std::string &s) { std::string r = s; r = _StrReplace(r, "\\040", " "); r = _StrReplace(r, "\\011", "\t"); r = _StrReplace(r, "\\012", "\n"); r = _StrReplace(r, "\\134", "\\"); return r; } static void _UpdateMountMap(MountMap &mm) { char szLine[512], szNode[512], szMount[512]; std::ifstream mounts(_MOUNTS_FILE); mm.clear(); while(mounts.getline(szLine, sizeof(szLine)).good()) { if((sscanf(szLine, "%s %s", szNode, szMount) == 2)) { std::string key(szNode); key = _UnescapeMountpointString(key); std::string val(szMount); val = _UnescapeMountpointString(val); mm.emplace(key, val); } } } ///////////////////////////////////////////////////////////////////////////// static bool _ProcessMounts(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm) { bool bChange = false; char szMntPoint[_countof(GFA_SYSINFO_PARTITION::szMntPoint)]; for(size_t i = 0; i < _countof(sdm.parts); ++i) { GFA_SYSINFO_PARTITION &part = sdm.parts[i]; if(part.valid) { bool bMountedOld = !!*part.szMntPoint; bool bMountedNew = _LookupMountPoint(mm, part.szDevNode, szMntPoint, sizeof(szMntPoint)); if(!bMountedOld && bMountedNew) { memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint)); _UpdatePartitionFsInfo(mm, part); _SetPartitionChange(sdm, i); bChange = true; } else if(bMountedOld && !bMountedNew) { memset(part.szMntPoint, 0, sizeof(part.szMntPoint)); _UpdatePartitionFsInfo(mm, part); _SetPartitionChange(sdm, i); bChange = true; } else if(bMountedOld && bMountedNew) { if(strcmp(part.szMntPoint, szMntPoint)) { memcpy(part.szMntPoint, szMntPoint, sizeof(part.szMntPoint)); _UpdatePartitionFsInfo(mm, part); _SetPartitionChange(sdm, i); bChange = true; } } } } return bChange; } #endif ///////////////////////////////////////////////////////////////////////////// static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI, bool &bStateTransition) { ctrlmsg_t nCtrlMsg; while(g_fRun && (nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI))) { switch(nCtrlMsg) { case GFA_APPCTRL_CTRLMSG_STOP: bStateTransition = g_fRun; g_fRun = false; TRACE("Received Message: STOP!\n"); break; case GFA_APPCTRL_CTRLMSG_PAUSE: if(!g_fPause) { bStateTransition = true; g_fPause = true; ::GfaIpcAppCtrlSetState(hAC, GIAS_Paused); TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Paused]); } break; case GFA_APPCTRL_CTRLMSG_RESUME: if(g_fPause) { bStateTransition = true; g_fPause = false; ::GfaIpcAppCtrlSetState(hAC, GIAS_Running); TRACE("%-8s: State: %s\n", "Me", g_pszStateNames[GIAS_Running]); } break; default: break; } } } #if 0 static const char* _GetFileSpec(const char *pszPathname) { if(pszPathname && *pszPathname) { const char *pSlash = strrchr(pszPathname, '/'); if(pSlash) return ++pSlash; else return pszPathname; } return NULL; } static bool _IsInternalEmmc(const char *pszDevNode) { if(pszDevNode) { const char *pszDevName = _GetFileSpec(pszDevNode); return _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART2) || _STR_EQUALS(pszDevName, _INTERNAL_EMMC_PART1); } return false; } static long long _NumberFromString(const char *pszString, int base, bool *pbErr) { if(!pszString || !*pszString) { if(pbErr) *pbErr = true; return 0; } char *endptr; long long nRet = strtoll(pszString, &endptr, base); if( (((nRet == LLONG_MAX) || (nRet == LLONG_MIN)) && (errno == ERANGE)) || ((nRet == 0) && (errno == EINVAL)) || (!!*endptr)) { if(pbErr) *pbErr = true; return 0; } if(pbErr) *pbErr = false; return nRet; } static long long _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, int base, bool *pbErr) { char szNum[64]; return _NumberFromString(_ReadDevPropertyValue(dev, pszKey, szNum, sizeof(szNum), false), base, pbErr); } static const char* _ReadDevPropertyValue(struct udev_device* dev, const char *pszKey, char *pszValue, size_t nCChValue, bool bTruncate) { if(!pszValue || !nCChValue) return NULL; memset(pszValue, 0, nCChValue); const char *pszVal = ::udev_device_get_property_value(dev, pszKey); if(pszVal) { size_t nLen = strlen(pszVal); if(nLen < nCChValue) { strcpy(pszValue, pszVal); return pszValue; } else if(bTruncate) { memcpy(pszValue, pszVal, nCChValue); pszValue[nCChValue - 1] = '\0'; return pszValue; } } return NULL; } static void _ProcessPartition(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm, struct udev_device* dev) { if(dev) { bool bChange; GFA_SYSINFO_PARTITION part; memset(&part, 0, sizeof(part)); part.nDiskIdx = -1; int nPartIdx = -1; int nDiskIdx = -1; const char *pszDevNode = ::udev_device_get_devnode(dev); if(!pszDevNode) return; // if(_IsInternalEmmc(pszDevNode)) // return; // skip internal emmc bool bInternalEmmc = _IsInternalEmmc(pszDevNode); strncpy(part.szDevNode, pszDevNode, sizeof(part.szDevNode) - 1); const char *pszAction = ::udev_device_get_action(dev); bool bAdd = _STR_EQUALS(pszAction, "add"); bool bRem = _STR_EQUALS(pszAction, "remove"); bool bEnum = !pszAction; if(bAdd || bEnum) { if(_LookupPartition(sdm, part.szDevNode) >= 0) return; part.internal = bInternalEmmc; _ReadDevPropertyValue(dev, "ID_FS_LABEL", part.szFsLabel, sizeof(part.szFsLabel), true); _ReadDevPropertyValue(dev, "ID_FS_TYPE", part.szFsType, sizeof(part.szFsType), true); _ReadDevPropertyValue(dev, "ID_FS_VERSION", part.szFsVersion, sizeof(part.szFsVersion), true); part.nKiBPartSize = _ReadDevPropertyValue(dev, "ID_PART_ENTRY_SIZE") / 2; _UpdatePartitionFsInfo(mm, part); struct udev_device* cur = dev; while((cur = ::udev_device_get_parent(cur))) { const char *pszSs = ::udev_device_get_subsystem(cur); const char *pszDt = ::udev_device_get_devtype(cur); const char *pszDn = ::udev_device_get_devnode(cur); if(!pszDn) break; if(_STR_EQUALS(pszSs, "usb") || _STR_EQUALS(pszSs, "block")) { if(_STR_EQUALS(pszDt, "disk")) { GFA_SYSINFO_DISK disk; memset(&disk, 0, sizeof(disk)); strncpy(disk.szDevNode, pszDn, sizeof(disk.szDevNode) - 1); disk.internal = bInternalEmmc; if(!_ReadDevPropertyValue(cur, "ID_NAME", disk.szName, sizeof(disk.szName), true)) _ReadDevPropertyValue(cur, "ID_MODEL", disk.szName, sizeof(disk.szName), true); _ReadDevPropertyValue(cur, "ID_VENDOR", disk.szVendor, sizeof(disk.szVendor), true); _ReadDevPropertyValue(cur, "ID_BUS", disk.szBus, sizeof(disk.szBus), true); disk.nVendorID = _ReadDevPropertyValue(cur, "ID_VENDOR_ID", 16); disk.nProductID = _ReadDevPropertyValue(cur, "ID_MODEL_ID", 16); if((nDiskIdx = _AddDisk(sdm, disk, bChange)) >= 0) { if((nPartIdx = _AddPartition(sdm, part, bChange)) < 0) { if(sdm.disks[nDiskIdx].nPartCount == 0) _RemoveDisk(sdm, nDiskIdx, bChange); break; } _DiskAddPartition(sdm, nDiskIdx, nPartIdx, bChange); _PartitionSetDisk(sdm, nPartIdx, nDiskIdx, bChange); _SetPartitionChange(sdm, nPartIdx); } break; } } } } else if(bRem) { if((nPartIdx = _LookupPartition(sdm, pszDevNode)) >= 0) { _RemovePartition(sdm, nPartIdx, bChange); _SetPartitionChange(sdm, nPartIdx); if((nDiskIdx = _PartitionGetDisk(sdm, nPartIdx)) >= 0) { if(!_DiskRemovePartition(sdm, nDiskIdx, nPartIdx, bChange)) { _RemoveDisk(sdm, nDiskIdx, bChange); } } } } } } static void _EnumStorageDevices(GFA_SYSINFO_STORAGE_DEVICE_MAP &sdm, MountMap &mm, struct udev *pUdev) { struct udev_enumerate *pEnum = ::udev_enumerate_new(pUdev); ::udev_enumerate_add_match_subsystem(pEnum, "block"); ::udev_enumerate_add_match_property(pEnum, "DEVTYPE", "partition"); ::udev_enumerate_scan_devices(pEnum); struct udev_list_entry *devices = ::udev_enumerate_get_list_entry(pEnum); struct udev_list_entry *entry; udev_list_entry_foreach(entry, devices) { const char *pszPath = ::udev_list_entry_get_name(entry); struct udev_device* dev = ::udev_device_new_from_syspath(pUdev, pszPath); _ProcessPartition(sdm, mm, dev); ::udev_device_unref(dev); } ::udev_enumerate_unref(pEnum); } #endif ///////////////////////////////////////////////////////////////////////////// static void _SigHandler(int sig) { UNUSED(sig); g_fPause = false; g_fRun = false; g_fZombie = false; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]) { int c; HAPPCTRL hAC = NULL; HAPPINFO hAI; CCycleTimer ct(_CYCLE_INTV), perfCnt(0U); cy_time_t wStart, wEnd, wCur = 0; std::string sDbUser, sDbPass; CMySqlInfo mySqlInfo; CStgDevInfo stgDevInfo; bool bStateTransition; ///////////////////////////////////////////////////////////////////////// // parse command line options while((c = getopt(argc, argv, "p:u:")) != -1) { switch(c) { case 'p': sDbPass = optarg; break; case 'u': sDbUser = optarg; break; } } ///////////////////////////////////////////////////////////////////////// // signal handling struct sigaction sa; memset(&sa, 0, sizeof(sa)); 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 { g_fZombie = true; if(!(hAC = ::GfaIpcAppCtrlAcquire(_APPID, _APPNAME, _CYCLE_INTV * 1000, _CYCLE_INTV * 3000))) { TRACE("GfaIpcAppCtrlAcquire failed\n"); break; } ::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing); if(!::GfaIpcAppCtrlCreateSysInfo(hAC)) { TRACE("GfaIpcAppCtrlCreateSysInfo failed\n"); break; } ::GfaIpcDumpSHMROT(); TRACE("My Name: %s\n", _APPNAME); TRACE("My AppID: %llu\n", _APPID); TRACE("My PID: %d\n", getpid()); TRACE("My Cycle: %d\n", _CYCLE_INTV); CMySqlInfo::EXEC_PARAMS myep = {hAC, sDbUser, sDbPass}; mySqlInfo.Create(&myep); CStgDevInfo::EXEC_PARAMS step = {hAC}; stgDevInfo.Create(&step); g_fZombie = false; g_fRun = true; ::GfaIpcAppCtrlSetState(hAC, GIAS_Running); mySqlInfo.Signal(CMySqlInfo::S_UpdateAll); stgDevInfo.Signal(CStgDevInfo::S_Init); } while(false); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // run while(g_fRun) { bStateTransition = false; ///////////////////////////////////////////////////////////////////// // trigger cycle timer ct.Trigger(); ///////////////////////////////////////////////////////////////////// // update app control info if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, wCur))) { _ProcessCtrlMessages(hAC, hAI, bStateTransition); } ///////////////////////////////////////////////////////////////////// // if not paused, do work if(g_fRun) { if(bStateTransition) { if(g_fPause) { mySqlInfo.Signal(CMySqlInfo::S_Pause); stgDevInfo.Signal(CStgDevInfo::S_Pause); } else { mySqlInfo.Signal(CMySqlInfo::S_Resume); stgDevInfo.Signal(CStgDevInfo::S_Resume); } } if(!g_fPause) { wStart = ct.GetMicroTick(); ::GfaIpcAppCtrlUpdateSysInfo(hAC); wEnd = ct.GetMicroTick(); wCur = wEnd - wStart; } ct.Sleep1(); } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // terminate mySqlInfo.Signal(CMySqlInfo::S_Terminate); stgDevInfo.Signal(CStgDevInfo::S_Terminate); mySqlInfo.Join(NULL); stgDevInfo.Join(NULL); if(g_fZombie) { if(hAC) ::GfaIpcAppCtrlSetState(hAC, GIAS_Zombie); TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Zombie)); pause(); } if(hAC) { ::GfaIpcAppCtrlSetState(hAC, GIAS_Terminating); ::GfaIpcAppCtrlReleaseSysInfo(hAC); ::GfaIpcAppCtrlRelease(hAC); } return 0; }