• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

ksycoca.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999-2000 Waldo Bastian <bastian@kde.org>
00003  *
00004  *  This library is free software; you can redistribute it and/or
00005  *  modify it under the terms of the GNU Library General Public
00006  *  License version 2 as published by the Free Software Foundation;
00007  *
00008  *  This library is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  *  Library General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU Library General Public License
00014  *  along with this library; see the file COPYING.LIB.  If not, write to
00015  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  *  Boston, MA 02110-1301, USA.
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 // The following limitations are in place:
00052 // Maximum length of a single string: 8192 bytes
00053 // Maximum length of a string list: 1024 strings
00054 // Maximum number of entries: 8192
00055 //
00056 // The purpose of these limitations is to limit the impact
00057 // of database corruption.
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, // m_str is 0, openDatabase must be called
00097         NoDatabase, // not found, so we opened a dummy one instead
00098         BadVersion, // it's opened, but it's not useable
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 // Read-only constructor
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    // We register with D-Bus _before_ we try to open the database.
00131    // This way we can be relatively sure that the KDE framework is
00132    // up and running (kdeinit, klauncher, kded) and
00133    // that the database is up to date.
00134 
00135    //   -> huh? -thiago
00136    //   This is because dcopserver was autostarted (via kdeinit) when trying to register to dcop. - David
00137    //   But the "launching kdeinit" case below takes care of it.
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      /* POSIX mandates only MAP_FAILED, but we are paranoid so check for
00179         null pointer too.  */
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      // No database file
00208      delete m_database;
00209      m_database = 0;
00210 
00211      databaseStatus = NoDatabase;
00212      if (openDummyIfNotFound)
00213      {
00214         // We open a dummy database instead.
00215         //kDebug(7011) << "No database, opening a dummy one.";
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 // Read-write constructor - only for KBuildSycoca
00233 KSycoca::KSycoca( bool /* dummy */ )
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/* don't open dummy db if not found */);
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       // Solaris has munmap(char*, size_t) and everything else should
00275       // be happy with a char* for munmap(void*, size_t)
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    // It is very important to delete all factories here
00289    // since they cache information about the database file
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     //kDebug() << "got a notifyDatabaseChanged signal !";
00312     // kded tells us the database file changed
00313     // Close the database and forget all about what we knew
00314     // The next call to any public method will recreate
00315     // everything that's needed.
00316     d->closeDatabase();
00317 
00318     // Now notify applications
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    //kDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16);
00328    m_str->device()->seek(offset);
00329    qint32 aType;
00330    *m_str >> aType;
00331    type = KSycocaType(aType);
00332    //kDebug(7011) << QString("KSycoca::found type %1").arg(aType);
00333    return m_str;
00334 }
00335 
00336 KSycocaFactoryList* KSycoca::factories()
00337 {
00338     return d->lstFactories;
00339 }
00340 
00341 // Warning, checkVersion rewinds to the beginning of m_str.
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 // If it returns true, we have a valid database and the stream has rewinded to the beginning
00360 // and past the version number.
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()) // we know the version is ok, but we must rewind the stream anyway
00367             return true;
00368     }
00369 
00370     closeDatabase(); // close the dummy one
00371     // Check if new database already available
00372     if( openDatabase(ifNotFound & IfNotFoundOpenDummy) ) {
00373         Q_ASSERT(m_str); // if a database was found then m_str shouldn't be 0
00374         if (checkVersion()) {
00375             // Database exists, and version is ok.
00376             return true;
00377         }
00378     }
00379 
00380     static bool triedLaunchingKdeinit = false;
00381     if ((ifNotFound & IfNotFoundRecreate) && !triedLaunchingKdeinit) { // try only once
00382         triedLaunchingKdeinit = true;
00383         // Well, if kdeinit is not running we need to launch it,
00384         // but otherwise we simply need to run kbuildsycoca to recreate the sycoca file.
00385         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.klauncher")) {
00386             kDebug(7011) << "We have no database.... launching kdeinit";
00387             KToolInvocation::klauncher(); // this calls startKdeinit
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         // Wait until the DBUS signal from kbuildsycoca
00395         QEventLoop eventLoop;
00396         QObject::connect(KSycoca::self(), SIGNAL(databaseChanged()), &eventLoop, SLOT(quit()));
00397         eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
00398 
00399         // Ok, the new database should be here now, open it.
00400         if (!openDatabase(ifNotFound & IfNotFoundOpenDummy)) {
00401             kDebug(7011) << "Still no database...";
00402             return false; // Still no database - uh oh
00403         }
00404         if (!checkVersion()) {
00405             kDebug(7011) << "Still outdated...";
00406             return false; // Still outdated - uh oh
00407         }
00408         return true;
00409     }
00410 
00411     return false;
00412 }
00413 
00414 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00415 {
00416     // Ensure we have a valid database (right version, and rewinded to beginning)
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             //kDebug(7011) << "KSycoca::findFactory(" << id << ") offset " << aOffset;
00432             m_str->device()->seek(aOffset);
00433             return m_str;
00434         }
00435     }
00436     return 0;
00437 }
00438 
00439 QString KSycoca::kfsstnd_prefixes()
00440 {
00441     // do not try to launch kbuildsycoca from here; this code is also called by kbuildsycoca.
00442    if (!d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) return "";
00443    qint32 aId;
00444    qint32 aOffset;
00445    // skip factories offsets
00446    while(true)
00447    {
00448       *m_str >> aId;
00449       if ( aId )
00450         *m_str >> aOffset;
00451       else
00452         break; // just read 0
00453    }
00454    // We now point to the header
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     // might need canonicalPath() ...
00500     if ( _fullpath.indexOf( *dirsit ) == 0 ) // path is dirs + relativePath
00501       sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs
00502   }
00503   if ( sRelativeFilePath.isEmpty() )
00504     kFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource);
00505   //else
00506     // debug code
00507     //kDebug(7011) << sRelativeFilePath;
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           // Rebuild the damned thing.
00522           if (QProcess::execute(KStandardDirs::findExe(KBUILDSYCOCA_EXENAME)) != 0)
00523               qWarning("ERROR: Running %s failed", KBUILDSYCOCA_EXENAME);
00524           // Do not wait until the DBUS signal from kbuildsycoca here.
00525           // It deletes m_str which is a problem when flagError is called during the KSycocaFactory ctor...
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"

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   WTF
  • KJSEmbed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  •   core
  • Phonon
  •   Backend
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal