
//OpenSCADA module DAQ.System file: da_smart.cpp
/***************************************************************************
 *   Copyright (C) 2005-2025 by Roman Savochenko, <roman@oscada.org>       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; version 2 of the License.               *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include <tsys.h>

#include "os_contr.h"
#include "da_smart.h"

#define SCSI_MAJOR 8
#define NVME_MAJOR 259

using namespace SystemCntr;

const char *HddSmart::smartval_cmd = "smartctl -A %s 2> /dev/null",	// "smartctl -A -v N,raw48 %s 2> /dev/null";
	*HddSmart::stdRE_str = "^\\s*(\\d+)\\s+([^\\s]+)\\s+0x[0-9a-fA-F]{4}\\s+\\d{3}\\s+\\d{3}\\s+[0-9\\-]{3}\\s+[^\\s]+\\s+[^\\s]+\\s+[^\\s]+\\s+([^\\n]+)",
	*HddSmart::logRE_str = "^(.+):\\s+([^\\n]+)";

//*************************************************
//* HddSmart                                      *
//*************************************************
HddSmart::HddSmart( )	{ }

HddSmart::~HddSmart( )	{ }

void HddSmart::getVal( TMdPrm *prm )
{
    char buf[256];
    TRegExp stdRE(stdRE_str), logRE(logRE_str), replRE(",", "g");

    string dev = prm->cfg("SUBT").getS();

    //SMART attributes
    vector<string> als;
    string cmd = TSYS::strMess(smartval_cmd,("/dev/"+dev+((dev.size()&&dev[0]=='s')?" -d ata":"")).c_str()),
	sId, sName, sVal;
    FILE *fp = popen(cmd.c_str(),"r");
    TArrayObj *stdEntr = NULL, *logEntr = NULL;
    while(fp && fgets(buf,sizeof(buf),fp) != NULL) {
	stdEntr = logEntr = NULL;
	if(((stdEntr=stdRE.match(buf)) && stdEntr->arSize() >= 3) ||	// Typical mode
	    ((logEntr=logRE.match(buf)) && logEntr->arSize() >= 2))	// NVMe Log and other log entries
	{
	    if(stdEntr && stdEntr->arSize() >= 3) {
		sId = stdEntr->arGet(1).getS();
		sName = stdEntr->arGet(2).getS()+" ("+sId+")";
		sVal = stdEntr->arGet(3).getS();
	    }
	    else if(logEntr && logEntr->arSize() >= 2) {
		sName = logEntr->arGet(1).getS();
		sId = TSYS::strEncode(sName, TSYS::oscdID);
		sVal = logEntr->arGet(2).getS();
	    }
	    if(!prm->vlPresent(sId)) {
		//Checking for no number symbols presence
		unsigned iCh = 0;
		for( ; iCh < sVal.size(); ++iCh) {
		    switch(sVal[iCh]) {
			case ',': case '0' ... '9': case 'x': case 'X': continue;
		    }
		    break;
		}
		((TElem*)prm->daData)->fldAdd(new TFld(sId.c_str(),sName.c_str(),
					(iCh<sVal.size())?TFld::String:TFld::Integer,TFld::NoWrite));
	    }

	    sVal = replRE.replace(sVal, "");

	    if(prm->vlAt(sId).at().fld().type() == TFld::Integer)
		prm->vlAt(sId).at().setI(strtoll(sVal.c_str(),NULL,0), 0, true);
	    else prm->vlAt(sId).at().setS(sVal, 0, true);

	    als.push_back(sId);
	}

	if(stdEntr) delete stdEntr;
	if(logEntr) delete logEntr;
    }
    if(fp && pclose(fp) == -1)
	mess_warning(prm->nodePath().c_str(), _("Closing the pipe %p error '%s (%d)'!"), fp, strerror(errno), errno);

    if(als.size()) {
	prm->daErr = "";
	//Checking to delete DAQ parameter's attributes
	for(int iP = 0; iP < (int)fldSize(); ++iP) {
	    unsigned iL;
	    for(iL = 0; iL < als.size(); iL++)
		if(fldAt(iP).name() == als[iL])
		    break;
	    if(iL >= als.size())
		try{ fldDel(iP); --iP; }
		catch(TError &err) { mess_warning(err.cat.c_str(), err.mess.c_str()); }
	}
    }
    else if(!prm->daErr.getVal().size()) {
	prm->setEval();
	prm->daErr = _("10:Device is not available.");
    }
}

void HddSmart::dList( vector<string> &list, TMdPrm *prm )
{
    int major, minor;
    char name[11];
    char buf[256];
    TRegExp stdRE(stdRE_str), logRE(logRE_str);

    FILE *f = fopen("/proc/partitions","r");
    if(f == NULL) return;

    while(fgets(buf,sizeof(buf),f) != NULL) {
	if(sscanf(buf,"%d %d %*d %10s",&major,&minor,name) != 3) continue;
	if((major != SCSI_MAJOR && major != NVME_MAJOR) || (minor%16)) continue;

	string cmd = TSYS::strMess(smartval_cmd,(string("/dev/")+name+((major==SCSI_MAJOR)?" -d ata":"")).c_str());
	FILE *fp = popen(cmd.c_str(), "r");
	if(fp) {
	    bool access_true = false;
	    while(fgets(buf,sizeof(buf),fp) != NULL) {
		if(!stdRE.test(buf) && !logRE.test(buf)) continue;
		access_true = true;
		break;
	    }
	    if(pclose(fp) == -1)
		mess_warning(mod->nodePath().c_str(), _("Closing the pipe %p error '%s (%d)'!"), fp, strerror(errno), errno);
	    if(access_true) list.push_back(name);
	}
    }
    if(fclose(f) != 0)
	mess_warning(mod->nodePath().c_str(), _("Closing the file %p error '%s (%d)'!"), f, strerror(errno), errno);
}
