servermanager.cpp
00001 /* 00002 Copyright (c) 2008 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "servermanager.h" 00021 #include "servermanager_p.h" 00022 00023 #include "agenttype.h" 00024 #include "agentbase.h" 00025 #include "agentmanager.h" 00026 #include "dbusconnectionpool.h" 00027 #ifndef Q_OS_WINCE 00028 #include "selftestdialog_p.h" 00029 #endif 00030 #include "session_p.h" 00031 #include "firstrun_p.h" 00032 00033 #include <KDebug> 00034 #include <KGlobal> 00035 #include <KLocale> 00036 00037 #include <QtDBus> 00038 #include <QTimer> 00039 00040 #include <boost/scoped_ptr.hpp> 00041 00042 #define AKONADI_CONTROL_SERVICE QLatin1String( "org.freedesktop.Akonadi.Control" ) 00043 #define AKONADI_SERVER_SERVICE QLatin1String( "org.freedesktop.Akonadi" ) 00044 00045 using namespace Akonadi; 00046 00047 class Akonadi::ServerManagerPrivate 00048 { 00049 public: 00050 ServerManagerPrivate() : 00051 instance( new ServerManager( this ) ), 00052 mState( ServerManager::NotRunning ), 00053 mSafetyTimer( new QTimer ), 00054 mFirstRunner( 0 ) 00055 { 00056 mState = instance->state(); 00057 mSafetyTimer->setSingleShot( true ); 00058 mSafetyTimer->setInterval( 30000 ); 00059 QObject::connect( mSafetyTimer.get(), SIGNAL(timeout()), instance, SLOT(timeout()) ); 00060 KGlobal::locale()->insertCatalog( QString::fromLatin1( "libakonadi" ) ); 00061 if ( mState == ServerManager::Running && Internal::clientType() == Internal::User ) 00062 mFirstRunner = new Firstrun( instance ); 00063 } 00064 00065 ~ServerManagerPrivate() 00066 { 00067 delete instance; 00068 } 00069 00070 void serviceOwnerChanged( const QString&, const QString&, const QString& ) 00071 { 00072 serverProtocolVersion = -1, 00073 checkStatusChanged(); 00074 } 00075 00076 void checkStatusChanged() 00077 { 00078 setState( instance->state() ); 00079 } 00080 00081 void setState( ServerManager::State state ) 00082 { 00083 00084 if ( mState != state ) { 00085 mState = state; 00086 emit instance->stateChanged( state ); 00087 if ( state == ServerManager::Running ) { 00088 emit instance->started(); 00089 if ( !mFirstRunner && Internal::clientType() == Internal::User ) 00090 mFirstRunner = new Firstrun( instance ); 00091 } else if ( state == ServerManager::NotRunning || state == ServerManager::Broken ) { 00092 emit instance->stopped(); 00093 } 00094 00095 if ( state == ServerManager::Starting || state == ServerManager::Stopping ) 00096 QMetaObject::invokeMethod( mSafetyTimer.get(), "start", Qt::QueuedConnection ); // in case we are in a different thread 00097 else 00098 QMetaObject::invokeMethod( mSafetyTimer.get(), "stop", Qt::QueuedConnection ); // in case we are in a different thread 00099 } 00100 } 00101 00102 void timeout() 00103 { 00104 if ( mState == ServerManager::Starting || mState == ServerManager::Stopping ) 00105 setState( ServerManager::Broken ); 00106 } 00107 00108 ServerManager *instance; 00109 static int serverProtocolVersion; 00110 ServerManager::State mState; 00111 boost::scoped_ptr<QTimer> mSafetyTimer; 00112 Firstrun *mFirstRunner; 00113 static Internal::ClientType clientType; 00114 }; 00115 00116 int ServerManagerPrivate::serverProtocolVersion = -1; 00117 Internal::ClientType ServerManagerPrivate::clientType = Internal::User; 00118 00119 K_GLOBAL_STATIC( ServerManagerPrivate, sInstance ) 00120 00121 ServerManager::ServerManager(ServerManagerPrivate * dd ) : 00122 d( dd ) 00123 { 00124 qRegisterMetaType<Akonadi::ServerManager::State>(); 00125 00126 QDBusServiceWatcher *watcher = new QDBusServiceWatcher( AKONADI_SERVER_SERVICE, 00127 DBusConnectionPool::threadConnection(), 00128 QDBusServiceWatcher::WatchForOwnerChange, this ); 00129 watcher->addWatchedService( AKONADI_CONTROL_SERVICE ); 00130 00131 connect( watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), 00132 this, SLOT(serviceOwnerChanged(QString,QString,QString)) ); 00133 00134 // AgentManager is dangerous to use for agents themselves 00135 if ( Internal::clientType() != Internal::User ) 00136 return; 00137 connect( AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)), SLOT(checkStatusChanged()) ); 00138 connect( AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)), SLOT(checkStatusChanged()) ); 00139 } 00140 00141 ServerManager * Akonadi::ServerManager::self() 00142 { 00143 return sInstance->instance; 00144 } 00145 00146 bool ServerManager::start() 00147 { 00148 const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ); 00149 const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ); 00150 if ( controlRegistered && serverRegistered ) 00151 return true; 00152 00153 // TODO: use AKONADI_CONTROL_SERVICE_LOCK instead once we depend on a recent enough Akonadi server 00154 const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE + QLatin1String( ".lock" ) ); 00155 if ( controlLockRegistered || controlRegistered ) { 00156 kDebug() << "Akonadi server is already starting up"; 00157 sInstance->setState( Starting ); 00158 return true; 00159 } 00160 00161 kDebug() << "executing akonadi_control"; 00162 const bool ok = QProcess::startDetached( QLatin1String( "akonadi_control" ) ); 00163 if ( !ok ) { 00164 kWarning() << "Unable to execute akonadi_control, falling back to D-Bus auto-launch"; 00165 QDBusReply<void> reply = DBusConnectionPool::threadConnection().interface()->startService( AKONADI_CONTROL_SERVICE ); 00166 if ( !reply.isValid() ) { 00167 kDebug() << "Akonadi server could not be started via D-Bus either: " 00168 << reply.error().message(); 00169 return false; 00170 } 00171 } 00172 sInstance->setState( Starting ); 00173 return true; 00174 } 00175 00176 bool ServerManager::stop() 00177 { 00178 QDBusInterface iface( AKONADI_CONTROL_SERVICE, 00179 QString::fromLatin1( "/ControlManager" ), 00180 QString::fromLatin1( "org.freedesktop.Akonadi.ControlManager" ) ); 00181 if ( !iface.isValid() ) 00182 return false; 00183 iface.call( QDBus::NoBlock, QString::fromLatin1( "shutdown" ) ); 00184 sInstance->setState( Stopping ); 00185 return true; 00186 } 00187 00188 void ServerManager::showSelfTestDialog( QWidget *parent ) 00189 { 00190 #ifndef Q_OS_WINCE 00191 Akonadi::SelfTestDialog dlg( parent ); 00192 dlg.hideIntroduction(); 00193 dlg.exec(); 00194 #endif 00195 } 00196 00197 bool ServerManager::isRunning() 00198 { 00199 return state() == Running; 00200 } 00201 00202 ServerManager::State ServerManager::state() 00203 { 00204 ServerManager::State previousState = NotRunning; 00205 if ( sInstance.exists() ) // be careful, this is called from the ServerManager::Private ctor, so using sInstance unprotected can cause infinite recursion 00206 previousState = sInstance->mState; 00207 00208 const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ); 00209 const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ); 00210 if ( controlRegistered && serverRegistered ) { 00211 // check if the server protocol is recent enough 00212 if ( sInstance.exists() ) { 00213 if ( Internal::serverProtocolVersion() >= 0 && 00214 Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion() ) 00215 return Broken; 00216 } 00217 00218 // AgentManager is dangerous to use for agents themselves 00219 if ( Internal::clientType() == Internal::User ) { 00220 // besides the running server processes we also need at least one resource to be operational 00221 AgentType::List agentTypes = AgentManager::self()->types(); 00222 foreach ( const AgentType &type, agentTypes ) { 00223 if ( type.capabilities().contains( QLatin1String( "Resource" ) ) ) 00224 return Running; 00225 } 00226 return Broken; 00227 } else { 00228 return Running; 00229 } 00230 } 00231 00232 // TODO: use AKONADI_CONTROL_SERVICE_LOCK instead once we depend on a recent enough Akonadi server 00233 const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE + QLatin1String( ".lock" ) ); 00234 if ( controlLockRegistered || controlRegistered ) { 00235 kDebug() << "Akonadi server is already starting up"; 00236 if ( previousState == Running ) 00237 return NotRunning; // we don't know if it's starting or stopping, probably triggered by someone else 00238 return previousState; 00239 } 00240 00241 if ( serverRegistered ) { 00242 kWarning() << "Akonadi server running without control process!"; 00243 return Broken; 00244 } 00245 00246 if ( previousState == Starting || previousState == Broken ) // valid cases where nothing might be running (yet) 00247 return previousState; 00248 return NotRunning; 00249 } 00250 00251 int Internal::serverProtocolVersion() 00252 { 00253 return ServerManagerPrivate::serverProtocolVersion; 00254 } 00255 00256 void Internal::setServerProtocolVersion( int version ) 00257 { 00258 ServerManagerPrivate::serverProtocolVersion = version; 00259 if ( sInstance.exists() ) 00260 sInstance->checkStatusChanged(); 00261 } 00262 00263 Internal::ClientType Internal::clientType() 00264 { 00265 return ServerManagerPrivate::clientType; 00266 } 00267 00268 void Internal::setClientType( ClientType type ) 00269 { 00270 ServerManagerPrivate::clientType = type; 00271 } 00272 00273 #include "servermanager.moc"