00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "ksycoca.h"
00020 #include "ksycocatype.h"
00021 #include "ksycocafactory.h"
00022 #include "ktoolinvocation.h"
00023 #include "kglobal.h"
00024 #include "kdebug.h"
00025 #include "kstandarddirs.h"
00026
00027 #include <QtCore/QDataStream>
00028 #include <QtCore/QCoreApplication>
00029 #include <QtCore/QFile>
00030 #include <QtCore/QBuffer>
00031 #include <QProcess>
00032 #include <QtDBus/QtDBus>
00033
00034 #include <config.h>
00035
00036 #include <stdlib.h>
00037 #include <fcntl.h>
00038
00039 #ifdef HAVE_SYS_MMAN_H
00040 #include <sys/mman.h>
00041 #endif
00042
00043 #ifdef Q_OS_SOLARIS
00044 extern "C" int madvise(caddr_t, size_t, int);
00045 #endif
00046
00047 #ifndef MAP_FAILED
00048 #define MAP_FAILED ((void *) -1)
00049 #endif
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059 class KSycocaPrivate
00060 {
00061 public:
00062 KSycocaPrivate()
00063 : databaseStatus( DatabaseNotOpen ),
00064 readError( false ),
00065 #ifdef Q_WS_WIN
00066 autoRebuild( false ),
00067 #else
00068 autoRebuild( true ),
00069 #endif
00070 sycoca_size( 0 ),
00071 sycoca_mmap( 0 ),
00072 timeStamp( 0 ),
00073 m_database( 0 ),
00074 updateSig( 0 ),
00075 lstFactories( 0 )
00076 {
00077 }
00078
00079 static void delete_ksycoca_self() {
00080 delete _self;
00081 _self = 0;
00082 }
00083
00084 bool checkVersion();
00085 bool openDatabase(bool openDummyIfNotFound=true);
00086 enum BehaviorIfNotFound {
00087 IfNotFoundDoNothing = 0,
00088 IfNotFoundOpenDummy = 1,
00089 IfNotFoundRecreate = 2
00090 };
00091 Q_DECLARE_FLAGS(BehaviorsIfNotFound, BehaviorIfNotFound)
00092 bool checkDatabase(BehaviorsIfNotFound ifNotFound);
00093 void closeDatabase();
00094
00095 enum {
00096 DatabaseNotOpen,
00097 NoDatabase,
00098 BadVersion,
00099 DatabaseOK } databaseStatus;
00100 bool readError;
00101 bool autoRebuild;
00102 size_t sycoca_size;
00103 const char *sycoca_mmap;
00104 quint32 timeStamp;
00105 QFile *m_database;
00106 QStringList changeList;
00107 QString language;
00108 quint32 updateSig;
00109 QStringList allResourceDirs;
00110 KSycocaFactoryList *lstFactories;
00111 static KSycoca *_self;
00112 };
00113 Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound)
00114
00115 KSycoca * KSycocaPrivate::_self = 0L;
00116
00117 int KSycoca::version()
00118 {
00119 return KSYCOCA_VERSION;
00120 }
00121
00122
00123 KSycoca::KSycoca()
00124 : m_str(0),
00125 d(new KSycocaPrivate)
00126 {
00127 QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KSycoca", "notifyDatabaseChanged",
00128 this, SLOT(notifyDatabaseChanged(QStringList)));
00129 KSycocaPrivate::_self = this;
00130
00131
00132
00133
00134
00135
00136
00137
00138 d->openDatabase();
00139 }
00140
00141 bool KSycocaPrivate::openDatabase( bool openDummyIfNotFound )
00142 {
00143 bool result = true;
00144
00145 sycoca_mmap = 0;
00146 QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00147 m_str = 0;
00148 QString path;
00149 QByteArray ksycoca_env = getenv("KDESYCOCA");
00150 if (ksycoca_env.isEmpty())
00151 path = KGlobal::dirs()->saveLocation("cache") + KSYCOCA_FILENAME;
00152 else
00153 path = QFile::decodeName(ksycoca_env);
00154
00155 kDebug(7011) << "Trying to open ksycoca from " << path;
00156 m_database = new QFile(path);
00157 bool bOpen = m_database->open( QIODevice::ReadOnly );
00158 if (!bOpen)
00159 {
00160 path = KStandardDirs::locate("services", KSYCOCA_FILENAME);
00161 if (!path.isEmpty())
00162 {
00163 kDebug(7011) << "Trying to open global ksycoca from " << path;
00164 delete m_database;
00165 m_database = new QFile(path);
00166 bOpen = m_database->open( QIODevice::ReadOnly );
00167 }
00168 }
00169
00170 if (bOpen)
00171 {
00172 fcntl(m_database->handle(), F_SETFD, FD_CLOEXEC);
00173 sycoca_size = m_database->size();
00174 #ifdef HAVE_MMAP
00175 sycoca_mmap = (const char *) mmap(0, sycoca_size,
00176 PROT_READ, MAP_SHARED,
00177 m_database->handle(), 0);
00178
00179
00180 if (sycoca_mmap == (const char*) MAP_FAILED || sycoca_mmap == 0)
00181 {
00182 kDebug(7011) << "mmap failed. (length = " << sycoca_size << ")";
00183 #endif
00184 m_str = new QDataStream(m_database);
00185 m_str->setVersion(QDataStream::Qt_3_1);
00186 sycoca_mmap = 0;
00187 #ifdef HAVE_MMAP
00188 }
00189 else
00190 {
00191 #ifdef HAVE_MADVISE
00192 (void) madvise((char*)sycoca_mmap, sycoca_size, MADV_WILLNEED);
00193 #endif
00194 QBuffer *buffer = new QBuffer;
00195 buffer->setData(QByteArray::fromRawData(sycoca_mmap, sycoca_size));
00196 buffer->open(QIODevice::ReadOnly);
00197 m_str = new QDataStream( buffer);
00198 m_str->setVersion(QDataStream::Qt_3_1);
00199 }
00200 #endif
00201 checkVersion();
00202 }
00203 else
00204 {
00205 kDebug(7011) << "Could not open ksycoca";
00206
00207
00208 delete m_database;
00209 m_database = 0;
00210
00211 databaseStatus = NoDatabase;
00212 if (openDummyIfNotFound)
00213 {
00214
00215
00216 QBuffer *buffer = new QBuffer;
00217 buffer->open(QIODevice::ReadWrite);
00218 m_str = new QDataStream(buffer);
00219 m_str->setVersion(QDataStream::Qt_3_1);
00220 *m_str << qint32(KSYCOCA_VERSION);
00221 *m_str << qint32(0);
00222 }
00223 else
00224 {
00225 result = false;
00226 }
00227 }
00228 lstFactories = new KSycocaFactoryList;
00229 return result;
00230 }
00231
00232
00233 KSycoca::KSycoca( bool )
00234 : m_str(0),
00235 d(new KSycocaPrivate)
00236 {
00237 QDBusConnection::sessionBus().registerObject("/ksycoca_building", this, QDBusConnection::ExportScriptableSlots);
00238 d->lstFactories = new KSycocaFactoryList;
00239 KSycocaPrivate::_self = this;
00240 }
00241
00242 KSycoca * KSycoca::self()
00243 {
00244 if (!KSycocaPrivate::_self) {
00245 qAddPostRoutine(KSycocaPrivate::delete_ksycoca_self);
00246 KSycocaPrivate::_self = new KSycoca;
00247 }
00248 return KSycocaPrivate::_self;
00249 }
00250
00251 KSycoca::~KSycoca()
00252 {
00253 d->closeDatabase();
00254 delete d;
00255 KSycocaPrivate::_self = 0L;
00256 }
00257
00258 bool KSycoca::isAvailable()
00259 {
00260 return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing);
00261 }
00262
00263 void KSycocaPrivate::closeDatabase()
00264 {
00265 QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00266 QIODevice *device = 0;
00267 if (m_str)
00268 device = m_str->device();
00269 #ifdef HAVE_MMAP
00270 if (device && sycoca_mmap)
00271 {
00272 QBuffer *buf = static_cast<QBuffer*>(device);
00273 buf->buffer().clear();
00274
00275
00276 munmap(const_cast<char*>(sycoca_mmap), sycoca_size);
00277 sycoca_mmap = 0;
00278 }
00279 #endif
00280
00281 delete m_str;
00282 m_str = 0;
00283 delete device;
00284 if (m_database != device)
00285 delete m_database;
00286 device = 0;
00287 m_database = 0;
00288
00289
00290 if ( lstFactories )
00291 qDeleteAll( *lstFactories );
00292 delete lstFactories;
00293 lstFactories = 0;
00294 databaseStatus = DatabaseNotOpen;
00295 }
00296
00297 void KSycoca::addFactory( KSycocaFactory *factory )
00298 {
00299 Q_ASSERT(d->lstFactories != 0);
00300 d->lstFactories->append(factory);
00301 }
00302
00303 bool KSycoca::isChanged(const char *type)
00304 {
00305 return self()->d->changeList.contains(type);
00306 }
00307
00308 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00309 {
00310 d->changeList = changeList;
00311
00312
00313
00314
00315
00316 d->closeDatabase();
00317
00318
00319 emit databaseChanged();
00320 }
00321
00322 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00323 {
00324 if ( !m_str )
00325 d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate | KSycocaPrivate::IfNotFoundOpenDummy);
00326 Q_ASSERT(m_str);
00327
00328 m_str->device()->seek(offset);
00329 qint32 aType;
00330 *m_str >> aType;
00331 type = KSycocaType(aType);
00332
00333 return m_str;
00334 }
00335
00336 KSycocaFactoryList* KSycoca::factories()
00337 {
00338 return d->lstFactories;
00339 }
00340
00341
00342 bool KSycocaPrivate::checkVersion()
00343 {
00344 QDataStream *m_str = KSycocaPrivate::_self->m_str;
00345 Q_ASSERT(m_str);
00346 m_str->device()->seek(0);
00347 qint32 aVersion;
00348 *m_str >> aVersion;
00349 if ( aVersion < KSYCOCA_VERSION ) {
00350 kWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher.";
00351 databaseStatus = BadVersion;
00352 return false;
00353 } else {
00354 databaseStatus = DatabaseOK;
00355 return true;
00356 }
00357 }
00358
00359
00360
00361 bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound)
00362 {
00363 QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00364 if (databaseStatus == DatabaseOK) {
00365 Q_ASSERT(m_str);
00366 if (checkVersion())
00367 return true;
00368 }
00369
00370 closeDatabase();
00371
00372 if( openDatabase(ifNotFound & IfNotFoundOpenDummy) ) {
00373 Q_ASSERT(m_str);
00374 if (checkVersion()) {
00375
00376 return true;
00377 }
00378 }
00379
00380 static bool triedLaunchingKdeinit = false;
00381 if ((ifNotFound & IfNotFoundRecreate) && !triedLaunchingKdeinit) {
00382 triedLaunchingKdeinit = true;
00383
00384
00385 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.klauncher")) {
00386 kDebug(7011) << "We have no database.... launching kdeinit";
00387 KToolInvocation::klauncher();
00388 } else {
00389 kDebug(7011) << "We have no database.... launching " << KBUILDSYCOCA_EXENAME;
00390 if (QProcess::execute(KStandardDirs::findExe(KBUILDSYCOCA_EXENAME)) != 0)
00391 qWarning("ERROR: Running KSycoca failed.");
00392 }
00393
00394
00395 QEventLoop eventLoop;
00396 QObject::connect(KSycoca::self(), SIGNAL(databaseChanged()), &eventLoop, SLOT(quit()));
00397 eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
00398
00399
00400 if (!openDatabase(ifNotFound & IfNotFoundOpenDummy)) {
00401 kDebug(7011) << "Still no database...";
00402 return false;
00403 }
00404 if (!checkVersion()) {
00405 kDebug(7011) << "Still outdated...";
00406 return false;
00407 }
00408 return true;
00409 }
00410
00411 return false;
00412 }
00413
00414 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00415 {
00416
00417 if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
00418 return 0;
00419 }
00420
00421 qint32 aId;
00422 qint32 aOffset;
00423 while(true) {
00424 *m_str >> aId;
00425 if (aId == 0) {
00426 kError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00427 break;
00428 }
00429 *m_str >> aOffset;
00430 if (aId == id) {
00431
00432 m_str->device()->seek(aOffset);
00433 return m_str;
00434 }
00435 }
00436 return 0;
00437 }
00438
00439 QString KSycoca::kfsstnd_prefixes()
00440 {
00441
00442 if (!d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) return "";
00443 qint32 aId;
00444 qint32 aOffset;
00445
00446 while(true)
00447 {
00448 *m_str >> aId;
00449 if ( aId )
00450 *m_str >> aOffset;
00451 else
00452 break;
00453 }
00454
00455 QString prefixes;
00456 KSycocaEntry::read(*m_str, prefixes);
00457 *m_str >> d->timeStamp;
00458 KSycocaEntry::read(*m_str, d->language);
00459 *m_str >> d->updateSig;
00460 KSycocaEntry::read(*m_str, d->allResourceDirs);
00461 return prefixes;
00462 }
00463
00464 quint32 KSycoca::timeStamp()
00465 {
00466 if (!d->timeStamp)
00467 (void) kfsstnd_prefixes();
00468 return d->timeStamp;
00469 }
00470
00471 quint32 KSycoca::updateSignature()
00472 {
00473 if (!d->timeStamp)
00474 (void) kfsstnd_prefixes();
00475 return d->updateSig;
00476 }
00477
00478 QString KSycoca::language()
00479 {
00480 if (d->language.isEmpty())
00481 (void) kfsstnd_prefixes();
00482 return d->language;
00483 }
00484
00485 QStringList KSycoca::allResourceDirs()
00486 {
00487 if (!d->timeStamp)
00488 (void) kfsstnd_prefixes();
00489 return d->allResourceDirs;
00490 }
00491
00492 #if 0
00493 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00494 {
00495 QString sRelativeFilePath;
00496 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00497 QStringList::ConstIterator dirsit = dirs.begin();
00498 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00499
00500 if ( _fullpath.indexOf( *dirsit ) == 0 )
00501 sRelativeFilePath = _fullpath.mid( (*dirsit).length() );
00502 }
00503 if ( sRelativeFilePath.isEmpty() )
00504 kFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource);
00505
00506
00507
00508 return sRelativeFilePath;
00509 }
00510 #endif
00511
00512 void KSycoca::flagError()
00513 {
00514 kWarning(7011) << "ERROR: KSycoca database corruption!";
00515 if (KSycocaPrivate::_self)
00516 {
00517 if (KSycocaPrivate::_self->d->readError)
00518 return;
00519 KSycocaPrivate::_self->d->readError = true;
00520 if (KSycocaPrivate::_self->d->autoRebuild) {
00521
00522 if (QProcess::execute(KStandardDirs::findExe(KBUILDSYCOCA_EXENAME)) != 0)
00523 qWarning("ERROR: Running %s failed", KBUILDSYCOCA_EXENAME);
00524
00525
00526 }
00527 }
00528 }
00529
00530 bool KSycoca::isBuilding()
00531 {
00532 return false;
00533 }
00534
00535 void KSycoca::disableAutoRebuild()
00536 {
00537 d->autoRebuild = false;
00538 }
00539
00540 bool KSycoca::readError()
00541 {
00542 bool b = false;
00543 if (KSycocaPrivate::_self)
00544 {
00545 b = KSycocaPrivate::_self->d->readError;
00546 KSycocaPrivate::_self->d->readError = false;
00547 }
00548 return b;
00549 }
00550
00551 #include "ksycoca.moc"