File indexing completed on 2024-05-19 04:49:44
0001 /**************************************************************************************** 0002 * Copyright (c) 2013 Konrad Zemek <konrad.zemek@gmail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #include "AmarokEmbeddedSqlConnection.h" 0018 0019 #include "core/support/Debug.h" 0020 0021 #include <ThreadWeaver/Thread> 0022 0023 #include <QEventLoop> 0024 #include <QFileSystemWatcher> 0025 #include <QMutexLocker> 0026 #include <QRandomGenerator> 0027 #include <QStringList> 0028 #include <QTemporaryFile> 0029 0030 using namespace StatSyncing; 0031 0032 AmarokEmbeddedSqlConnection::AmarokEmbeddedSqlConnection( const QFileInfo &mysqld, 0033 const QDir &datadir ) 0034 : ImporterSqlConnection() 0035 , m_mysqld( mysqld ) 0036 , m_datadir( datadir ) 0037 { 0038 connect( &m_shutdownTimer, &QTimer::timeout, 0039 this, &AmarokEmbeddedSqlConnection::stopServer ); 0040 m_shutdownTimer.setSingleShot( true ); 0041 } 0042 0043 AmarokEmbeddedSqlConnection::~AmarokEmbeddedSqlConnection() 0044 { 0045 if( isTransaction() ) 0046 rollback(); 0047 stopServer(); 0048 } 0049 0050 QSqlDatabase 0051 AmarokEmbeddedSqlConnection::connection() 0052 { 0053 Q_ASSERT( this->thread() == ThreadWeaver::Thread::currentThread() ); 0054 0055 QMutexLocker lock( &m_srvMutex ); 0056 0057 // The server's already running; only refresh its shutdown timer 0058 if( m_srv.state() == QProcess::Running ) 0059 { 0060 m_shutdownTimer.start( SERVER_SHUTDOWN_AFTER ); 0061 return QSqlDatabase::database( m_connectionName ); 0062 } 0063 0064 QTemporaryFile pidFile( QDir::temp().filePath( "amarok_importer-XXXXXX.pid" ) ); 0065 QTemporaryFile socket( QDir::temp().filePath( "amarok_importer-XXXXXX.socket" ) ); 0066 pidFile.open(); 0067 socket.open(); 0068 0069 // Get random port in range 3307 - 65535 0070 const int port = ( QRandomGenerator::global()->generate() % ( 65536 - 3307 ) ) + 3307; 0071 0072 QSqlDatabase::removeDatabase( m_connectionName ); 0073 QSqlDatabase db = QSqlDatabase::addDatabase( "QMYSQL", m_connectionName ); 0074 db.setDatabaseName ( "amarok" ); 0075 db.setHostName ( "localhost" ); 0076 db.setUserName ( "root" ); 0077 db.setPassword ( "" ); 0078 db.setPort ( port ); 0079 db.setConnectOptions( "UNIX_SOCKET=" + QFileInfo( socket ).absoluteFilePath() ); 0080 0081 if( startServer( port, QFileInfo( socket ).absoluteFilePath(), 0082 QFileInfo( pidFile ).absoluteFilePath() ) ) 0083 { 0084 // Give tempfiles ownership over to mysqld 0085 pidFile.setAutoRemove( false ); 0086 socket.setAutoRemove( false ); 0087 0088 m_shutdownTimer.start( SERVER_SHUTDOWN_AFTER ); 0089 } 0090 0091 db.open(); 0092 return db; 0093 } 0094 0095 bool 0096 AmarokEmbeddedSqlConnection::startServer( const int port, const QString &socketPath, 0097 const QString &pidPath ) 0098 { 0099 DEBUG_BLOCK 0100 Q_ASSERT( this->thread() == ThreadWeaver::Thread::currentThread() ); 0101 0102 if( !m_mysqld.isExecutable() ) 0103 { 0104 warning() << __PRETTY_FUNCTION__ << m_mysqld.absoluteFilePath() 0105 << "is not executable"; 0106 return false; 0107 } 0108 0109 if( !m_datadir.isReadable() ) 0110 { 0111 warning() << __PRETTY_FUNCTION__ << m_datadir.absolutePath() << "is not readable"; 0112 return false; 0113 } 0114 0115 QEventLoop loop; 0116 QFileSystemWatcher watcher; 0117 QTimer timer; 0118 0119 // Set conditions on which we stop waiting for the startup 0120 connect( &timer, &QTimer::timeout, 0121 &loop, &QEventLoop::quit, Qt::QueuedConnection ); 0122 connect( &watcher, &QFileSystemWatcher::fileChanged, 0123 &loop, &QEventLoop::quit, Qt::QueuedConnection ); 0124 connect( &m_srv, QOverload<QProcess::ProcessError>::of( &QProcess::errorOccurred ), 0125 &loop, &QEventLoop::quit, Qt::QueuedConnection ); 0126 0127 // Important: we use modification of pidfile as a cue that the server is ready 0128 // This is consistent with behavior of mysqld startup scripts 0129 watcher.addPath( pidPath ); 0130 timer.start( SERVER_START_TIMEOUT ); 0131 0132 const QStringList args = QStringList() 0133 << "--no-defaults" 0134 << "--port=" + QString::number( port ) 0135 << "--datadir=" + m_datadir.absolutePath() 0136 << "--default-storage-engine=MyISAM" 0137 << "--skip-grant-tables" 0138 << "--myisam-recover-options=FORCE" 0139 << "--key-buffer-size=16777216" 0140 << "--character-set-server=utf8" 0141 << "--collation-server=utf8_bin" 0142 << "--skip-innodb" 0143 << "--bind-address=localhost" 0144 << "--socket=" + socketPath 0145 << "--pid-file=" + pidPath; 0146 0147 m_srv.start( m_mysqld.absoluteFilePath(), args ); 0148 debug() << __PRETTY_FUNCTION__ << m_mysqld.absoluteFilePath() + ' ' + args.join(' '); 0149 0150 // Wait for any of the startup conditions to be true 0151 loop.exec(); 0152 0153 if( m_srv.state() != QProcess::Running ) 0154 { 0155 warning() << __PRETTY_FUNCTION__ << "error starting server application:" 0156 << m_srv.errorString(); 0157 return false; 0158 } 0159 0160 return true; 0161 } 0162 0163 void 0164 AmarokEmbeddedSqlConnection::stopServer() 0165 { 0166 DEBUG_BLOCK 0167 Q_ASSERT( this->thread() == ThreadWeaver::Thread::currentThread() ); 0168 0169 QMutexLocker lock( &m_srvMutex ); 0170 if( isTransaction() || m_srv.state() == QProcess::NotRunning ) 0171 return; 0172 0173 m_shutdownTimer.stop(); 0174 QSqlDatabase::removeDatabase( m_connectionName ); 0175 0176 m_srv.terminate(); 0177 if( !m_srv.waitForFinished() ) 0178 { 0179 m_srv.kill(); 0180 m_srv.waitForFinished(); 0181 } 0182 }