// rdmaint.cpp // // A Utility for running periodic system maintenance. // // (C) Copyright 2008-2018 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // 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., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MainObject::MainObject(QObject *parent) :QObject(parent) { QString err_msg; // // Initialize Data Structures // maint_verbose=false; maint_system=false; // // Open the Database // rda=new RDApplication("rdmaint","rdmaint",RDMAINT_USAGE,this); if(!rda->open(&err_msg)) { fprintf(stderr,"rdmaint: %s\n",(const char *)err_msg); exit(1); } // // Read Command Options // if(rda->cmdSwitch()->keys()>3) { fprintf(stderr,"\n"); fprintf(stderr,"%s",RDMAINT_USAGE); fprintf(stderr,"\n"); exit(2); } for(unsigned i=0;icmdSwitch()->keys();i++) { if(rda->cmdSwitch()->key(i)=="--verbose") { maint_verbose=true; rda->cmdSwitch()->setProcessed(i,true); } if(rda->cmdSwitch()->key(i)=="--system") { maint_system=true; rda->cmdSwitch()->setProcessed(i,true); } if(!rda->cmdSwitch()->processed(i)) { fprintf(stderr,"rdmaint: unknown command option \"%s\"\n", (const char *)rda->cmdSwitch()->key(i)); exit(2); } } // // RIPC Connection // connect(rda,SIGNAL(userChanged()),this,SLOT(userData())); rda->ripc()-> connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password()); } void MainObject::userData() { if(maint_system) { RunSystemMaintenance(); } else { RunLocalMaintenance(); } exit(0); } void MainObject::RunSystemMaintenance() { QString sql; RDSqlQuery *q; sql="update VERSION set LAST_MAINT_DATETIME=now()"; q=new RDSqlQuery(sql); delete q; PurgeCuts(); PurgeLogs(); PurgeElr(); PurgeGpioEvents(); PurgeWebapiAuths(); PurgeStacks(); RehashCuts(); } void MainObject::RunLocalMaintenance() { PurgeDropboxes(); } void MainObject::PurgeCuts() { QString sql; RDSqlQuery *q; RDSqlQuery *q1; RDSqlQuery *q2; QDateTime dt=QDateTime(QDate::currentDate(),QTime::currentTime()); sql=QString("select ")+ "NAME,"+ // 00 "CUT_SHELFLIFE,"+ // 01 "DELETE_EMPTY_CARTS "+ // 02 "from GROUPS where "+ "CUT_SHELFLIFE>=0"; q=new RDSqlQuery(sql); while(q->next()) { sql=QString("select ")+ "CART.NUMBER,"+ // 00 "CUTS.CUT_NAME "+ // 01 "from CUTS left join CART "+ "on CUTS.CART_NUMBER=CART.NUMBER where "+ "(CART.GROUP_NAME=\""+RDEscapeString(q->value(0).toString())+"\")&&"+ "(CUTS.END_DATETIME<\""+ RDEscapeString(dt.addDays(-q->value(1).toInt()).toString("yyyy-MM-dd"))+ " 00:00:00\")"; q1=new RDSqlQuery(sql); while(q1->next()) { RDCart *cart=new RDCart(q1->value(0).toUInt()); if(cart->removeCut(rda->station(),rda->user(),q1->value(1).toString(), rda->config())) { rda->config()-> log("rdmaint",RDConfig::LogInfo,QString().sprintf("purged cut %s", (const char *)q1->value(1).toString())); } else { rda->config()-> log("rdmaint",RDConfig::LogErr,QString(). sprintf("unable to purge cut %s: audio deletion error", (const char *)q1->value(1).toString())); } if(q->value(2).toString()=="Y") { // Delete Empty Cart sql=QString("select CUT_NAME from CUTS where ")+ QString().sprintf("CART_NUMBER=%u",q1->value(0).toUInt()); q2=new RDSqlQuery(sql); if(!q2->first()) { cart->remove(rda->station(),rda->user(),rda->config()); rda->config()-> log("rdmaint",RDConfig::LogInfo,QString(). sprintf("deleted purged cart %06u",cart->number())); } delete q2; } delete cart; } delete q1; } delete q; } void MainObject::PurgeLogs() { QString sql; RDSqlQuery *q; QDateTime dt=QDateTime(QDate::currentDate(),QTime::currentTime()); sql=QString("select NAME from LOGS where ")+ "(PURGE_DATE!=\"0000-00-00\")&&"+ "(PURGE_DATE is not null)&&"+ "(PURGE_DATE<\""+dt.date().toString("yyyy-MM-dd")+"\")"; q=new RDSqlQuery(sql); while(q->next()) { rda->config()-> log("rdmain",RDConfig::LogInfo,QString().sprintf("purged log %s", (const char *)q->value(0).toString())); RDLog *log=new RDLog(q->value(0).toString()); log->remove(rda->station(),rda->user(),rda->config()); delete log; } delete q; } void MainObject::PurgeElr() { QString sql; RDSqlQuery *q; QDateTime dt=QDateTime(QDate::currentDate(),QTime::currentTime()); sql=QString("select ")+ "NAME,"+ "ELR_SHELFLIFE "+ "from SERVICES where "+ "ELR_SHELFLIFE>=0"; q=new RDSqlQuery(sql); while(q->next()) { sql=QString("delete from ELR_LINES where ")+ "SERVICE_NAME=\""+RDEscapeString(q->value(0).toString())+"\" && "+ "EVENT_DATETIME<\""+ dt.addDays(-q->value(1).toInt()).toString("yyyy-MM-dd")+" 00:00:00\""; RDSqlQuery::apply(sql); } delete q; } void MainObject::PurgeDropboxes() { QString sql; RDSqlQuery *q; RDSqlQuery *q1; sql=QString("select ")+ "DROPBOX_PATHS.FILE_PATH,"+ // 00 "DROPBOX_PATHS.ID "+ // 01 "from DROPBOXES left join DROPBOX_PATHS "+ "on (DROPBOXES.ID=DROPBOX_PATHS.DROPBOX_ID) where "+ "DROPBOXES.STATION_NAME=\""+RDEscapeString(rda->config()->stationName())+ "\""; q=new RDSqlQuery(sql); while(q->next()) { if(!QFile::exists(q->value(0).toString())) { sql=QString("delete from DROPBOX_PATHS where ")+ QString().sprintf("ID=%d",q->value(1).toInt()); q1=new RDSqlQuery(sql); delete q1; } } delete q; } void MainObject::PurgeGpioEvents() { QString sql; RDSqlQuery *q; sql=QString("delete from GPIO_EVENTS where ")+ "EVENT_DATETIME<\""+ QDate::currentDate().addDays(-30).toString("yyyy-MM-dd")+" 00:00:00\""; q=new RDSqlQuery(sql); delete q; } void MainObject::PurgeWebapiAuths() { QString sql; RDSqlQuery *q; sql=QString("delete from WEBAPI_AUTHS where EXPIRATION_DATETIMEnext()) { artistsep=q->value(0).toInt(); } delete q; sql="select MAX(TITLE_SEP) from EVENTS"; q=new RDSqlQuery(sql); if(q->next()) { titlesep=q->value(0).toInt(); } delete q; stacksize=(artistsepnext()) { sql=QString().sprintf("select MAX(SCHED_STACK_ID) from STACK_LINES where ")+ "SERVICE_NAME=\""+RDEscapeString(q->value(0).toString())+"\""; q1=new RDSqlQuery(sql); if (q1->next()) { stackid=q1->value(0).toUInt(); if (stackid-stacksize > 0) { sql=QString("select ID from STACK_LINES where ")+ "SERVICE_NAME=\""+RDEscapeString(q->value(0).toString())+"\" && "+ QString().sprintf("SCHED_STACK_ID<=%d",stackid-stacksize); q2=new RDSqlQuery(sql); while(q2->next()) { sql=QString("delete from STACK_SCHED_CODES where ")+ QString().sprintf("STACK_LINES_ID=%u",q2->value(0).toUInt()); RDSqlQuery::apply(sql); } delete q2; sql=QString("delete from STACK_LINES where ")+ "SERVICE_NAME=\""+RDEscapeString(q->value(0).toString())+"\" && "+ QString().sprintf("SCHED_STACK_ID<=%d",stackid-stacksize); RDSqlQuery::apply(sql); sql=QString("update STACK_LINES set ")+ QString().sprintf("SCHED_STACK_ID=SCHED_STACK_ID-%d where ", stackid-stacksize)+ "SERVICE_NAME=\""+RDEscapeString(q->value(0).toString())+"\""; q2=new RDSqlQuery(sql); delete q2; } } delete q1; } delete q; } void MainObject::RehashCuts() { QString sql; RDSqlQuery *q; RDRehash::ErrorCode err; sql="select CUT_NAME from CUTS where SHA1_HASH is null limit 100"; q=new RDSqlQuery(sql); while(q->next()) { if((err=RDRehash::rehash(rda->station(),rda->user(),rda->config(), RDCut::cartNumber(q->value(0).toString()), RDCut::cutNumber(q->value(0).toString())))!=RDRehash::ErrorOk) { rda->config()-> log("rdmaint", RDConfig::LogErr,QString().sprintf("failed to rehash cut %s [%s]", (const char *)q->value(0).toString(), (const char *)RDRehash::errorText(err))); } else { if(maint_verbose) { fprintf(stderr,"rehashed cut \"%s\"\n", (const char *)q->value(0).toString()); } } sleep(1); } delete q; } int main(int argc,char *argv[]) { QApplication a(argc,argv,false); new MainObject(); return a.exec(); }