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