/* DESC: A script that can be used for a longtime logging of a NetModule Router. 
 * The upper part of the script is the logger template. The lower part is the real programm, where you describe the settings and workflow.
 * Copyright (C) 2014 NetModule AG, Switzerland
 */
    
template logger {
    workfile="current.csv";
    serialNumber=nb_status('system').SERIAL_NUMBER;
    currentFilename="";
    currentLogStart=0; // the current logfile was startet at this time
    intervall=5; // new logentry every # seconds
    logfileIntervall=3600; // new logfile every # seconds
    // due to the fact that we cannot see the amount of free space in the sdk
    // we need to improvise and add up the current filesizes   
    currentLogSize=0; // in Bytes
    maxLogSize=15000000;  // in Bytes
    freespace=1000000; // in Bytes
    # give the constructor the absolute path to store the Loggs into
    void logger(string path) {
        dh=opendir(path);    
        if(dh == -1) {
            if ( mkdir(path,0666) == -1 ); {
                nb_syslog("Could not create dir: %s. Exiting",path);
                exit(1);
            }
        } else {
        nb_syslog("directory already existent: %s", path);
        closedir(dh);
        }
        this.path = path;
        this.fields = mkarray();
        namepart=strftime("%Y%m%d_%H%M%S_start.csv",localtime(time()));
        this.currentFilename=sprintf("%s_%s",this.serialNumber, namepart);
    }

    int jiffy() {
        sys = sysinfo();
        u = struct_get(sys, "uptime");

        if (is_void(u) || u < 1) {
            return 0;
        } else {
            return u;
        }
    } 

    int addField(string fieldname) {
        if (strlen(fieldname) > 0 ) {
            this.fields = array_merge(this.fields, fieldname);
            return 0;
        } else {
            nb_syslog("Fieldname to short, not adding");
            return -1;
        }
    }

    int addFields(array localFields) {
       for (i=0; i < length(localFields); i++) {
            if (this.addField(localFields[i]) == -1 ) return -1;
       }   
    return 0;

    }

    int newLogfile() {
        // check if file if we have a work file an rename it to currentFile
        fd=fopen(sprintf("%s%s", this.path,this.workfile),"r" );
        if(fd) {
            // we have a current workfile lets rename it
            fclose(fd);
            if(!rename(sprintf("%s%s",this.path,this.workfile), sprintf("%s%s",this.path,this.currentFilename))){
                nb_syslog("Could not rename %s to %s, %s will be deleted",this.workfile, this.currentFilename,this.workfile);
                remove(sprintf("%s%s",this.path,this.workfile));  
            }
        } 
        //set new current Filename as we stored the old one away
        namepart=strftime("%Y%m%d_%H%M%S.csv",localtime(time()));
        this.currentFilename=sprintf("%s_%s",this.serialNumber,namepart);
        this.currentLogStart=this.jiffy();
        //creating Header   
        header="";
        for (i=0; i < length(this.fields); i++) {
            header=sprintf('%s%s;',header,this.fields[i]);   
        }   

        if(this.addLineToFile(header) == -1 ) {
            nb_syslog("writing of header to file: %s%s:", this.path,this.workfile);
            return -1;
        } else {
            return 0;
        }
    }

    int addLineToFile(string line) {
        fd=fopen(sprintf("%s%s", this.path,this.workfile),"a" );
        if (fd < 0 ) {
            nb_syslog("could not open file %s%s", this.path, this.workfile);
        }
      
        if(!fwrite(fd,sprintf("%s\n",line))) {
            nb_syslog("writing of line to file: %s%s:", this.path,this.workfile);
            fclose(fd);
            return -1;
        } else {
            fclose(fd);
            return 0;
        }
    } 

    int filesize(string file) {
        fd=fopen(file,"r");
        if (fd) {
            fseek(fd,-1);
            size=ftell(fd);
            fclose(fd);
            return size;
        } else {
            return -1;
        }
    }


    int logNow()  {
        //get current status output
        sections=mkarray("system","wan","wwan","wlan","gnss","lan","openvpn","ipsec","dio","license", "hotspot");
        status=mkstruct();
        for (i=0;i<length(sections);i++) {
            status=struct_merge(status,nb_status(sections[i]));
        }
        
        line="";
        for (i=0; i < length(this.fields); i++) {
            value=struct_get(status,this.fields[i]);
            if (strlen(value) == 0) {   
                value=sprintf("n/a");
            }
            line=sprintf('%s%s;',line,value);   
        }

        if(this.addLineToFile(line) == -1 ) {
            nb_syslog("writing of line to file failed: %s%s:", this.path,this.workfile);
            return -1;
        } else {
            return 0;
        }
    }

    int logRotate() {
        // get all files in path
        nb_syslog("Starting Logrotate");
        files=mkarray();
        handle = opendir(this.path);
        if (handle != -1) {
            while ((entry = readdir(handle))) {
                if (entry == ".") continue;
                if (entry == "..") continue;
                if (entry == this.workfile) continue;
                files=array_merge(files,entry);
            }
            closedir(handle);
        } else {
            nb_syslog("cannot open directory %s", this.path);
        }
        // sort old to new
        qsort(files);
        // files=array_reverse(files);
        // get current filesizes
        this.currenLogSize=0;
        sizes=mkarray();
        for(i=0;i<length(files);i++){
            size[i]=this.filesize(sprintf("%s%s",this.path,files[i]));
            this.currentLogSize=this.currentLogSize+size[i]; 
        }
        nb_syslog("Current Logsize: %i byte",this.currentLogSize);
        //if we don't have enough space delete enough old ones to have enough space
    return 0;
    }
} 
// ######################################################
// ######################################################
// ######################################################

l = new logger("/logger/");
// Timestamp of the Logentry
l.addField("SYSTEM_TIME");

// serialNumber added for customer
l.addField("SERIAL_NUMBER");

// LTE Connection Fields
// Can be cloneed with MOBILE2 in case more LTE Connections are used
// Arbitrary Strength Unit (ASU) https://en.wikipedia.org/wiki/Mobile_phone_signal#ASU
l.addField("MOBILE1_SIGNAL_LEVEL");
// received signal code power  https://en.wikipedia.org/wiki/Received_signal_code_power
l.addField("MOBILE1_SIGNAL_RSCP");
// Received signal strength indication https://en.wikipedia.org/wiki/Received_signal_strength_indication           
l.addField("MOBILE1_SIGNAL_RSSI");          
// Service Type: LTE/HSPA/3G/2G/EDGE        
l.addField("MOBILE1_SERVICE_TYPE");
// NETWORK ID of the LTE Network we are connected to
l.addField("MOBILE1_LAI");
// Area Code in  LTE Network
l.addField("MOBILE1_LAC");
// Cell ID we are connected to
l.addField("MOBILE1_CID");
// Is the SIM registered in Home or Roaming
l.addField("MOBILE1_REGISTRATION_STATE");


// WAN Data Connection field
// Connection that is current default Gateway
l.addField("WAN_HOTLINK");
// Per WANLINK/LTE Connection:
// Current State: up/down/dialin
l.addField("WANLINK1_STATE");
// Current upload and download rates 
l.addField("WANLINK1_DOWNLOAD_RATE");
l.addField("WANLINK1_UPLOAD_RATE");
// Bytes that are already down- or uploaded.
l.addField("WANLINK1_DATA_DOWNLOADED");
l.addField("WANLINK1_DATA_UPLOADED");

// WLAN Fields
// WLAN Clients Attached to the Accesspoint
l.addField("WLAN1_STATION_COUNT");
l.addField("HS1_TOTAL_STATION_COUNT");


MAX_WLAN_CLIENTS=51;
for (j=1; j<MAX_WLAN_CLIENTS; j++) {
l.addField(sprintf("HS1_STATION%i_MAC",j));
}
 
l.newLogfile();
l.intervall=30; // log every 30 sec
l.logfileIntervall=7200; // create a new file every 2 hours

// ######################################################
// ######################################################
// ######################################################
// ######################################################


while (true) {
    l.logNow();
    sleep(l.intervall);
    // lets see if we need a new logfile
    if (l.currentLogStart+l.logfileIntervall<l.jiffy()) {
       l.newLogfile();
       l.logRotate();
    }
}
exit(0);
