|
MythTV
0.26-pre
|
00001 #include <climits> 00002 #include <stdlib.h> 00003 #include <stdio.h> 00004 #include <sys/types.h> 00005 #include <sys/stat.h> 00006 #include <unistd.h> 00007 00008 #include <QDir> 00009 #include <QFile> 00010 #include <QRegExp> 00011 #include <QDateTime> 00012 #include <QSqlError> 00013 #include <QSqlRecord> 00014 00015 #include "dbutil.h" 00016 #include "mythcorecontext.h" 00017 #include "storagegroup.h" 00018 #include "mythmiscutil.h" 00019 #include "mythdb.h" 00020 #include "mythdirs.h" 00021 #include "mythlogging.h" 00022 #include "mythsystem.h" 00023 #include "exitcodes.h" 00024 00025 #define LOC QString("DBUtil: ") 00026 00027 const int DBUtil::kUnknownVersionNumber = INT_MIN; 00028 00032 DBUtil::DBUtil(void) 00033 : m_versionString(QString::null), m_versionMajor(-1), m_versionMinor(-1), 00034 m_versionPoint(-1) 00035 { 00036 } 00037 00042 QString DBUtil::GetDBMSVersion(void) 00043 { 00044 if (m_versionString.isEmpty()) 00045 QueryDBMSVersion(); 00046 return m_versionString; 00047 } 00048 00061 int DBUtil::CompareDBMSVersion(int major, int minor, int point) 00062 { 00063 if (m_versionMajor < 0) 00064 if (!ParseDBMSVersion()) 00065 return kUnknownVersionNumber; 00066 00067 int result = 0; 00068 int version[3] = {m_versionMajor, m_versionMinor, m_versionPoint}; 00069 int compareto[3] = {major, minor, point}; 00070 for (int i = 0; i < 3 && !result; i++) 00071 { 00072 if ((version[i] > -1) || (compareto[i] != 0)) 00073 result = version[i] - compareto[i]; 00074 } 00075 00076 return result; 00077 } 00078 00082 bool DBUtil::IsNewDatabase(void) 00083 { 00084 const QStringList tables = GetTables(); 00085 const int size = tables.size(); 00086 // Usually there will be a single table called schemalock, but check for 00087 // no tables, also, just in case. 00088 return (((size == 1) && (tables.at(0).endsWith(".`schemalock`"))) || 00089 (size == 0)); 00090 } 00091 00096 bool DBUtil::IsBackupInProgress(void) 00097 { 00098 QString backupStartTimeStr = 00099 gCoreContext->GetSetting("BackupDBLastRunStart"); 00100 QString backupEndTimeStr = gCoreContext->GetSetting("BackupDBLastRunEnd"); 00101 00102 if (backupStartTimeStr.isEmpty()) 00103 { 00104 LOG(VB_DATABASE, LOG_ERR, "DBUtil::BackupInProgress(): No start time " 00105 "found, database backup is not in progress."); 00106 return false; 00107 } 00108 00109 backupStartTimeStr.replace(" ", "T"); 00110 00111 QDateTime backupStartTime = 00112 QDateTime::fromString(backupStartTimeStr, Qt::ISODate); 00113 00114 // No end time set 00115 if (backupEndTimeStr.isEmpty()) 00116 { 00117 // If DB Backup started less then 10 minutes ago, assume still running 00118 if (backupStartTime.secsTo(QDateTime::currentDateTime()) < 600) 00119 { 00120 LOG(VB_DATABASE, LOG_INFO, 00121 QString("DBUtil::BackupInProgress(): Found " 00122 "database backup start time of %1 which was %2 seconds " 00123 "ago, therefore it appears the backup is still running.") 00124 .arg(backupStartTimeStr) 00125 .arg(backupStartTime.secsTo(QDateTime::currentDateTime()))); 00126 return true; 00127 } 00128 else 00129 { 00130 LOG(VB_DATABASE, LOG_ERR, QString("DBUtil::BackupInProgress(): " 00131 "Database backup started at %1, but no end time was found. " 00132 "The backup started %2 seconds ago and should have " 00133 "finished by now therefore it appears it is not running .") 00134 .arg(backupStartTimeStr) 00135 .arg(backupStartTime.secsTo(QDateTime::currentDateTime()))); 00136 return false; 00137 } 00138 } 00139 else 00140 { 00141 backupEndTimeStr.replace(" ", "T"); 00142 00143 QDateTime backupEndTime = 00144 QDateTime::fromString(backupEndTimeStr, Qt::ISODate); 00145 00146 if (backupEndTime >= backupStartTime) 00147 { 00148 LOG(VB_DATABASE, LOG_ERR, 00149 QString("DBUtil::BackupInProgress(): Found " 00150 "database backup end time of %1 later than start time " 00151 "of %2, therefore backup is not running.") 00152 .arg(backupEndTimeStr).arg(backupStartTimeStr)); 00153 return false; 00154 } 00155 else if (backupStartTime.secsTo(QDateTime::currentDateTime()) > 600) 00156 { 00157 LOG(VB_DATABASE, LOG_ERR, 00158 QString("DBUtil::BackupInProgress(): " 00159 "Database backup started at %1, but has not ended yet. " 00160 "The backup started %2 seconds ago and should have " 00161 "finished by now therefore it appears it is not running") 00162 .arg(backupStartTimeStr) 00163 .arg(backupStartTime.secsTo(QDateTime::currentDateTime()))); 00164 return false; 00165 } 00166 else 00167 { 00168 // start > end and started less than 10 minutes ago 00169 LOG(VB_DATABASE, LOG_INFO, QString("DBUtil::BackupInProgress(): " 00170 "Database backup started at %1, and is still running.") 00171 .arg(backupStartTimeStr)); 00172 return true; 00173 } 00174 } 00175 00176 // Shouldn't get here 00177 return false; 00178 } 00179 00205 MythDBBackupStatus DBUtil::BackupDB(QString &filename, bool disableRotation) 00206 { 00207 filename = QString(); 00208 00209 #ifdef USING_MINGW 00210 LOG(VB_GENERAL, LOG_CRIT, "Database backups disabled on Windows."); 00211 return kDB_Backup_Disabled; 00212 #else 00213 00214 if (gCoreContext->GetNumSetting("DisableAutomaticBackup", 0)) 00215 { 00216 LOG(VB_GENERAL, LOG_CRIT, 00217 "Database backups disabled. Skipping backup."); 00218 return kDB_Backup_Disabled; 00219 } 00220 00221 if (IsNewDatabase()) 00222 { 00223 LOG(VB_GENERAL, LOG_CRIT, "New database detected. Skipping backup."); 00224 return kDB_Backup_Empty_DB; 00225 } 00226 00227 QString backupScript = GetShareDir() + "mythconverg_backup.pl"; 00228 backupScript = gCoreContext->GetSetting("DatabaseBackupScript", 00229 backupScript); 00230 00231 if (!QFile::exists(backupScript)) 00232 { 00233 LOG(VB_GENERAL, LOG_CRIT, QString("Database backup script does " 00234 "not exist: %1").arg(backupScript)); 00235 backupScript = QString::null; 00236 } 00237 00238 bool result = false; 00239 MSqlQuery query(MSqlQuery::InitCon()); 00240 00241 gCoreContext->SaveSettingOnHost("BackupDBLastRunStart", 00242 QDateTime::currentDateTime() 00243 .toString("yyyy-MM-dd hh:mm:ss"), NULL); 00244 00245 if (!backupScript.isEmpty()) 00246 { 00247 result = DoBackup(backupScript, filename, disableRotation); 00248 if (!result) 00249 LOG(VB_GENERAL, LOG_CRIT, "Script-based database backup failed. " 00250 "Retrying with internal backup."); 00251 } 00252 00253 if (!result) 00254 result = DoBackup(filename); 00255 00256 gCoreContext->SaveSettingOnHost("BackupDBLastRunEnd", 00257 QDateTime::currentDateTime() 00258 .toString("yyyy-MM-dd hh:mm:ss"), NULL); 00259 00260 if (query.isConnected()) 00261 { 00262 QString dbTag("BackupDB"); 00263 query.prepare("DELETE FROM housekeeping WHERE tag = :TAG ;"); 00264 query.bindValue(":TAG", dbTag); 00265 if (!query.exec()) 00266 MythDB::DBError("DBUtil::BackupDB", query); 00267 00268 query.prepare("INSERT INTO housekeeping(tag,lastrun) " 00269 "values(:TAG ,now()) ;"); 00270 query.bindValue(":TAG", dbTag); 00271 if (!query.exec()) 00272 MythDB::DBError("DBUtil::BackupDB", query); 00273 } 00274 00275 if (result) 00276 return kDB_Backup_Completed; 00277 00278 return kDB_Backup_Failed; 00279 #endif // USING_MINGW 00280 } 00281 00293 bool DBUtil::CheckTables(const bool repair, const QString options) 00294 { 00295 MSqlQuery query(MSqlQuery::InitCon()); 00296 if (!query.isConnected()) 00297 return false; 00298 00299 const QStringList all_tables = GetTables(QStringList("MyISAM")); 00300 00301 if (all_tables.empty()) 00302 return true; 00303 00304 QString sql = QString("CHECK TABLE %1 %2;").arg(all_tables.join(", ")) 00305 .arg(options); 00306 00307 LOG(VB_GENERAL, LOG_CRIT, "Checking database tables."); 00308 if (!query.exec(sql)) 00309 { 00310 MythDB::DBError("DBUtil Checking Tables", query); 00311 return false; 00312 } 00313 00314 QStringList tables = CheckRepairStatus(query); 00315 bool result = true; 00316 if (!tables.empty()) 00317 { 00318 LOG(VB_GENERAL, LOG_CRIT, QString("Found crashed database table(s): %1") 00319 .arg(tables.join(", "))); 00320 if (repair == true) 00321 // If RepairTables() repairs the crashed tables, return true 00322 result = RepairTables(tables); 00323 else 00324 result = false; 00325 } 00326 00327 return result; 00328 } 00329 00347 bool DBUtil::RepairTables(const QStringList &tables) 00348 { 00349 MSqlQuery query(MSqlQuery::InitCon()); 00350 if (!query.isConnected()) 00351 return false; 00352 00353 QString all_tables = tables.join(", "); 00354 LOG(VB_GENERAL, LOG_CRIT, QString("Repairing database tables: %1") 00355 .arg(all_tables)); 00356 00357 QString sql = QString("REPAIR TABLE %1;").arg(all_tables); 00358 if (!query.exec(sql)) 00359 { 00360 MythDB::DBError("DBUtil Repairing Tables", query); 00361 return false; 00362 } 00363 00364 QStringList bad_tables = CheckRepairStatus(query); 00365 bool result = true; 00366 if (!bad_tables.empty()) 00367 { 00368 LOG(VB_GENERAL, LOG_CRIT, 00369 QString("Unable to repair crashed table(s): %1") 00370 .arg(bad_tables.join(", "))); 00371 result = false; 00372 } 00373 return result; 00374 } 00375 00394 QStringList DBUtil::CheckRepairStatus(MSqlQuery &query) 00395 { 00396 QStringList tables; 00397 QSqlRecord record = query.record(); 00398 int table_index = record.indexOf("Table"); 00399 int type_index = record.indexOf("Msg_type"); 00400 int text_index = record.indexOf("Msg_text"); 00401 QString table, type, text, previous_table; 00402 bool ok = true; 00403 while (query.next()) 00404 { 00405 table = query.value(table_index).toString(); 00406 type = query.value(type_index).toString(); 00407 text = query.value(text_index).toString(); 00408 if (table != previous_table) 00409 { 00410 if (!ok) 00411 { 00412 tables.append(previous_table); 00413 ok = true; 00414 } 00415 previous_table = table; 00416 } 00417 // If the final row shows status OK, the table is now good 00418 if ("status" == type.toLower() && "ok" == text.toLower()) 00419 ok = true; 00420 else if ("error" == type.toLower() || 00421 ("status" == type.toLower() && "ok" != text.toLower())) 00422 ok = false; 00423 } 00424 // Check the last table in the list 00425 if (!ok) 00426 tables.append(table); 00427 return tables; 00428 } 00429 00435 QStringList DBUtil::GetTables(const QStringList &engines) 00436 { 00437 QStringList result; 00438 00439 MSqlQuery query(MSqlQuery::InitCon()); 00440 if (!query.isConnected()) 00441 return result; 00442 00443 QString sql = "SELECT CONCAT('`', INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA, " 00444 " '`.`', INFORMATION_SCHEMA.TABLES.TABLE_NAME, " 00445 " '`') AS `TABLE_NAME` " 00446 " FROM INFORMATION_SCHEMA.TABLES " 00447 " WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = DATABASE() " 00448 " AND INFORMATION_SCHEMA.TABLES.TABLE_TYPE = 'BASE TABLE'"; 00449 if (!engines.empty()) 00450 sql.append(QString(" AND INFORMATION_SCHEMA.TABLES.ENGINE IN ('%1')") 00451 .arg(engines.join("', '"))); 00452 if (!query.exec(sql)) 00453 { 00454 MythDB::DBError("DBUtil Finding Tables", query); 00455 return result; 00456 } 00457 00458 while (query.next()) 00459 { 00460 result.append(query.value(0).toString()); 00461 } 00462 00463 return result; 00464 } 00465 00478 QString DBUtil::CreateBackupFilename(QString prefix, QString extension) 00479 { 00480 QDateTime now = QDateTime::currentDateTime(); 00481 QString time = now.toString("yyyyMMddhhmmss"); 00482 return QString("%1-%2%3").arg(prefix).arg(time).arg(extension); 00483 } 00484 00494 QString DBUtil::GetBackupDirectory() 00495 { 00496 QString directory; 00497 StorageGroup sgroup("DB Backups", gCoreContext->GetHostName()); 00498 QStringList dirList = sgroup.GetDirList(); 00499 if (dirList.size()) 00500 { 00501 directory = sgroup.FindNextDirMostFree(); 00502 00503 if (!QDir(directory).exists()) 00504 { 00505 LOG(VB_FILE, LOG_INFO, "GetBackupDirectory() - ignoring " + 00506 directory + ", using /tmp"); 00507 directory = QString::null; 00508 } 00509 } 00510 00511 if (directory.isNull()) 00512 // Rather than use kDefaultStorageDir, the default for 00513 // FindNextDirMostFree() when no dirs are defined for the StorageGroup, 00514 // use /tmp as it's possible that kDefaultStorageDir doesn't exist 00515 // and (at least on *nix) less possible that /tmp doesn't exist 00516 directory = "/tmp"; 00517 00518 return directory; 00519 } 00520 00529 bool DBUtil::CreateTemporaryDBConf( 00530 const QString &privateinfo, QString &filename) 00531 { 00532 bool ok = true; 00533 filename = createTempFile("/tmp/mythtv_db_backup_conf_XXXXXX"); 00534 const QByteArray tmpfile = filename.toLocal8Bit(); 00535 const DatabaseParams dbParams = gCoreContext->GetDatabaseParams(); 00536 const QString dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer"); 00537 00538 FILE *fp = fopen(tmpfile.constData(), "w"); 00539 if (!fp) 00540 { 00541 LOG(VB_GENERAL, LOG_ERR, LOC + 00542 QString("Unable to create temporary " 00543 "configuration file for creating DB backup: %1") 00544 .arg(tmpfile.constData())); 00545 filename = ""; 00546 ok = false; 00547 } 00548 else 00549 { 00550 chmod(tmpfile.constData(), S_IRUSR); 00551 00552 QByteArray outarr = privateinfo.toLocal8Bit(); 00553 fprintf(fp, "%s", outarr.constData()); 00554 00555 if (fclose(fp)) 00556 { 00557 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error closing '%1'") 00558 .arg(tmpfile.constData()) + ENO); 00559 } 00560 } 00561 00562 return ok; 00563 } 00564 00571 bool DBUtil::DoBackup(const QString &backupScript, QString &filename, 00572 bool disableRotation) 00573 { 00574 DatabaseParams dbParams = gCoreContext->GetDatabaseParams(); 00575 QString dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer"); 00576 QString backupDirectory = GetBackupDirectory(); 00577 QString backupFilename = CreateBackupFilename(dbParams.dbName + "-" + 00578 dbSchemaVer, ".sql"); 00579 QString scriptArgs = gCoreContext->GetSetting("BackupDBScriptArgs"); 00580 QString rotate = ""; 00581 if (disableRotation) 00582 { 00583 if (!(scriptArgs.contains("rotate", Qt::CaseInsensitive))) 00584 rotate = "rotate=-1"; 00585 } 00586 00587 00588 QString privateinfo = 00589 QString("DBHostName=%1\nDBPort=%2\n" 00590 "DBUserName=%3\nDBPassword=%4\n" 00591 "DBName=%5\nDBSchemaVer=%6\n" 00592 "DBBackupDirectory=%7\nDBBackupFilename=%8\n%9\n") 00593 .arg(dbParams.dbHostName).arg(dbParams.dbPort) 00594 .arg(dbParams.dbUserName).arg(dbParams.dbPassword) 00595 .arg(dbParams.dbName).arg(dbSchemaVer) 00596 .arg(backupDirectory).arg(backupFilename).arg(rotate); 00597 QString tempDatabaseConfFile = QString::null; 00598 bool hastemp = CreateTemporaryDBConf(privateinfo, tempDatabaseConfFile); 00599 if (!hastemp) 00600 LOG(VB_GENERAL, LOG_ERR, LOC + "Attempting backup, anyway."); 00601 00602 LOG(VB_GENERAL, LOG_ERR, QString("Backing up database with script: '%1'") 00603 .arg(backupScript)); 00604 00605 QString command = backupScript + " " + scriptArgs + " " + 00606 tempDatabaseConfFile; 00607 uint status = myth_system(command, kMSDontBlockInputDevs|kMSAnonLog); 00608 00609 if (hastemp) 00610 { 00611 QByteArray tmpfile = tempDatabaseConfFile.toLocal8Bit(); 00612 unlink(tmpfile.constData()); 00613 } 00614 00615 if (status != GENERIC_EXIT_OK) 00616 { 00617 LOG(VB_GENERAL, LOG_ERR, LOC + 00618 QString("Error backing up database: %1 (%2)") 00619 .arg(command).arg(status)); 00620 filename = "__FAILED__"; 00621 return false; 00622 } 00623 00624 LOG(VB_GENERAL, LOG_CRIT, "Database Backup complete."); 00625 00626 QDir dir(backupDirectory, backupFilename + "*"); 00627 uint numfiles = dir.count(); 00628 if (numfiles < 1) 00629 { 00630 // If no file begins with the suggested filename, don't show the backup 00631 // filename in the GUI message -- the script probably used some other 00632 // filename 00633 filename = ""; 00634 LOG(VB_FILE, LOG_ERR, LOC + 00635 QString("No files beginning with the suggested database backup " 00636 "filename '%1' were found in '%2'.") 00637 .arg(backupFilename).arg(backupDirectory)); 00638 } 00639 else 00640 { 00641 filename = dir.path() + "/" + dir[0];; 00642 if (numfiles > 1) 00643 { 00644 LOG(VB_FILE, LOG_ERR, LOC + 00645 QString("Multiple files beginning with the suggested database " 00646 "backup filename '%1' were found in '%2'. " 00647 "Assuming the first is the backup.") 00648 .arg(backupFilename).arg(backupDirectory)); 00649 } 00650 } 00651 00652 if (!filename.isEmpty()) 00653 { 00654 LOG(VB_GENERAL, LOG_CRIT, QString("Backed up database to file: '%1'") 00655 .arg(filename)); 00656 } 00657 00658 return true; 00659 } 00660 00667 bool DBUtil::DoBackup(QString &filename) 00668 { 00669 DatabaseParams dbParams = gCoreContext->GetDatabaseParams(); 00670 QString dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer"); 00671 QString backupDirectory = GetBackupDirectory(); 00672 00673 QString command; 00674 QString compressCommand(""); 00675 QString extension = ".sql"; 00676 if (QFile::exists("/bin/gzip")) 00677 compressCommand = "/bin/gzip"; 00678 else if (QFile::exists("/usr/bin/gzip")) 00679 compressCommand = "/usr/bin/gzip"; 00680 else 00681 LOG(VB_GENERAL, LOG_CRIT, "Neither /bin/gzip nor /usr/bin/gzip exist. " 00682 "The database backup will be uncompressed."); 00683 00684 QString backupFilename = CreateBackupFilename( 00685 dbParams.dbName + "-" + dbSchemaVer, extension); 00686 QString backupPathname = backupDirectory + "/" + backupFilename; 00687 00688 QString privateinfo = QString( 00689 "[client]\npassword=%1\n[mysqldump]\npassword=%2\n") 00690 .arg(dbParams.dbPassword).arg(dbParams.dbPassword); 00691 QString tempExtraConfFile = QString::null; 00692 if (!CreateTemporaryDBConf(privateinfo, tempExtraConfFile)) 00693 return false; 00694 00695 QString portArg = ""; 00696 if (dbParams.dbPort > 0) 00697 portArg = QString(" --port='%1'").arg(dbParams.dbPort); 00698 command = QString("mysqldump --defaults-extra-file='%1' --host='%2'%3" 00699 " --user='%4' --add-drop-table --add-locks" 00700 " --allow-keywords --complete-insert" 00701 " --extended-insert --lock-tables --no-create-db --quick" 00702 " '%5' > '%6' 2>/dev/null") 00703 .arg(tempExtraConfFile).arg(dbParams.dbHostName) 00704 .arg(portArg).arg(dbParams.dbUserName) 00705 .arg(dbParams.dbName).arg(backupPathname); 00706 00707 LOG(VB_FILE, LOG_INFO, QString("Backing up database with command: '%1'") 00708 .arg(command)); 00709 LOG(VB_GENERAL, LOG_CRIT, QString("Backing up database to file: '%1'") 00710 .arg(backupPathname)); 00711 00712 uint status = myth_system(command, kMSDontBlockInputDevs|kMSAnonLog); 00713 00714 QByteArray tmpfile = tempExtraConfFile.toLocal8Bit(); 00715 unlink(tmpfile.constData()); 00716 00717 if (status != GENERIC_EXIT_OK) 00718 { 00719 LOG(VB_GENERAL, LOG_ERR, LOC + 00720 QString("Error backing up database: '%1' (%2)") 00721 .arg(command).arg(status)); 00722 filename = "__FAILED__"; 00723 return false; 00724 } 00725 00726 if (compressCommand != "") 00727 { 00728 LOG(VB_GENERAL, LOG_CRIT, "Compressing database backup file."); 00729 compressCommand += " " + backupPathname; 00730 status = myth_system(compressCommand, kMSDontBlockInputDevs); 00731 00732 if (status != GENERIC_EXIT_OK) 00733 { 00734 LOG(VB_GENERAL, LOG_CRIT, 00735 "Compression failed, backup file will remain uncompressed."); 00736 } 00737 else 00738 { 00739 backupPathname += ".gz"; 00740 00741 LOG(VB_GENERAL, LOG_CRIT, QString("Database Backup filename: '%1'") 00742 .arg(backupPathname)); 00743 } 00744 } 00745 00746 LOG(VB_GENERAL, LOG_CRIT, "Database Backup complete."); 00747 00748 filename = backupPathname; 00749 return true; 00750 } 00751 00756 bool DBUtil::QueryDBMSVersion(void) 00757 { 00758 // Allow users to override the string provided by the database server in 00759 // case the value was changed to an unrecognizable string by whomever 00760 // compiled the MySQL server 00761 QString dbmsVersion = gCoreContext->GetSetting("DBMSVersionOverride"); 00762 00763 if (dbmsVersion.isEmpty()) 00764 { 00765 MSqlQuery query(MSqlQuery::InitCon()); 00766 query.prepare("SELECT VERSION();"); 00767 if (!query.exec() || !query.next()) 00768 { 00769 LOG(VB_GENERAL, LOG_ERR, LOC + 00770 "Unable to determine MySQL version."); 00771 MythDB::DBError("DBUtil Querying DBMS version", query); 00772 dbmsVersion = QString::null; 00773 } 00774 else 00775 dbmsVersion = query.value(0).toString(); 00776 } 00777 m_versionString = dbmsVersion; 00778 00779 return !m_versionString.isEmpty(); 00780 } 00781 00785 bool DBUtil::ParseDBMSVersion() 00786 { 00787 if (m_versionString.isEmpty()) 00788 if (!QueryDBMSVersion()) 00789 return false; 00790 00791 bool ok; 00792 QString section; 00793 int pos = 0, i = 0; 00794 int version[3] = {-1, -1, -1}; 00795 QRegExp digits("(\\d+)"); 00796 00797 while ((i < 3) && ((pos = digits.indexIn(m_versionString, pos)) > -1)) 00798 { 00799 section = digits.cap(1); 00800 pos += digits.matchedLength(); 00801 version[i] = section.toInt(&ok, 10); 00802 if (!ok) 00803 version[i] = -1; 00804 i++; 00805 } 00806 00807 m_versionMajor = version[0]; 00808 m_versionMinor = version[1]; 00809 m_versionPoint = version[2]; 00810 00811 return m_versionMajor > -1; 00812 } 00813 00817 int DBUtil::CountClients(void) 00818 { 00819 int count = 0; 00820 00821 MSqlQuery query(MSqlQuery::InitCon()); 00822 if (!query.isConnected()) 00823 { 00824 LOG(VB_GENERAL, LOG_DEBUG, "Not connected to DB"); 00825 return count; 00826 } 00827 00828 if (!query.exec("SHOW PROCESSLIST;")) 00829 { 00830 MythDB::DBError("DBUtil CountClients", query); 00831 return count; 00832 } 00833 00834 QSqlRecord record = query.record(); 00835 int db_index = record.indexOf("db"); 00836 QString dbName = gCoreContext->GetDatabaseParams().dbName; 00837 QString inUseDB; 00838 00839 while (query.next()) 00840 { 00841 inUseDB = query.value(db_index).toString(); 00842 if (inUseDB == dbName) 00843 ++count; 00844 } 00845 00846 // On average, each myth program has 4 database connections, 00847 // but we round up just in case a new program is loading: 00848 count = (count + 3)/4; 00849 00850 LOG(VB_GENERAL, LOG_DEBUG, 00851 QString("DBUtil::CountClients() found %1").arg(count)); 00852 00853 return count; 00854 } 00855 00859 bool DBUtil::TryLockSchema(MSqlQuery &query, uint timeout_secs) 00860 { 00861 query.prepare("SELECT GET_LOCK('schemaLock', :TIMEOUT)"); 00862 query.bindValue(":TIMEOUT", timeout_secs); 00863 return query.exec() && query.first() && query.value(0).toBool(); 00864 } 00865 00866 void DBUtil::UnlockSchema(MSqlQuery &query) 00867 { 00868 query.prepare("SELECT RELEASE_LOCK('schemaLock')"); 00869 query.exec(); 00870 } 00871 00872 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1