• Skip to content
  • Skip to link menu
KDE 4.1 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 "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 
00041 #ifdef Q_OS_WIN
00042 /*
00043  on windows we use KMemFile (QSharedMemory) to avoid problems
00044  with mmap (can't delete a mmap'd file)
00045 */ 
00046 #undef HAVE_MMAP
00047 #endif
00048 
00049 #ifdef HAVE_SYS_MMAN_H
00050 #include <sys/mman.h>
00051 #endif
00052 
00053 #ifdef Q_OS_SOLARIS
00054 extern "C" int madvise(caddr_t, size_t, int);
00055 #endif
00056 
00057 #ifndef MAP_FAILED
00058 #define MAP_FAILED ((void *) -1)
00059 #endif
00060 
00061 // The following limitations are in place:
00062 // Maximum length of a single string: 8192 bytes
00063 // Maximum length of a string list: 1024 strings
00064 // Maximum number of entries: 8192
00065 //
00066 // The purpose of these limitations is to limit the impact
00067 // of database corruption.
00068 
00069 class KSycocaPrivate
00070 {
00071 public:
00072     KSycocaPrivate()
00073         : databaseStatus( DatabaseNotOpen ),
00074           readError( false ),
00075           autoRebuild( true ),
00076           sycoca_size( 0 ),
00077           sycoca_mmap( 0 ),
00078           timeStamp( 0 ),
00079           m_database( 0 ),
00080           m_dummyBuffer(0),
00081           updateSig( 0 ),
00082           lstFactories( 0 )
00083     {
00084     }
00085 
00086     static void delete_ksycoca_self() {
00087         delete _self;
00088         _self = 0;
00089     }
00090 
00091     bool checkVersion();
00092     bool openDatabase(bool openDummyIfNotFound=true);
00093     enum BehaviorIfNotFound {
00094         IfNotFoundDoNothing = 0,
00095         IfNotFoundOpenDummy = 1,
00096         IfNotFoundRecreate = 2
00097     };
00098     Q_DECLARE_FLAGS(BehaviorsIfNotFound, BehaviorIfNotFound)
00099     bool checkDatabase(BehaviorsIfNotFound ifNotFound);
00100     void closeDatabase();
00101 
00102     enum {
00103         DatabaseNotOpen, // m_str is 0, openDatabase must be called
00104         NoDatabase, // not found, so we opened a dummy one instead
00105         BadVersion, // it's opened, but it's not useable
00106         DatabaseOK } databaseStatus;
00107     bool readError;
00108     bool autoRebuild;
00109     size_t sycoca_size;
00110     const char *sycoca_mmap;
00111     quint32 timeStamp;
00112 #ifdef Q_OS_WIN
00113     KMemFile *m_database;
00114 #else
00115     QFile *m_database;
00116 #endif
00117     QBuffer* m_dummyBuffer;
00118     QStringList changeList;
00119     QString language;
00120     quint32 updateSig;
00121     QStringList allResourceDirs;
00122     KSycocaFactoryList *lstFactories;
00123     static KSycoca *_self;
00124 };
00125 Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound)
00126 
00127 KSycoca * KSycocaPrivate::_self = 0L;
00128 
00129 int KSycoca::version()
00130 {
00131    return KSYCOCA_VERSION;
00132 }
00133 
00134 // Read-only constructor
00135 KSycoca::KSycoca()
00136   : m_str(0),
00137     d(new KSycocaPrivate)
00138 {
00139    QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KSycoca", "notifyDatabaseChanged",
00140                                this, SLOT(notifyDatabaseChanged(QStringList)));
00141    KSycocaPrivate::_self = this;
00142    // We register with D-Bus _before_ we try to open the database.
00143    // This way we can be relatively sure that the KDE framework is
00144    // up and running (kdeinit, klauncher, kded) and
00145    // that the database is up to date.
00146 
00147    //   -> huh? -thiago
00148    //   This is because dcopserver was autostarted (via kdeinit) when trying to register to dcop. - David
00149    //   But the "launching kdeinit" case below takes care of it.
00150    d->openDatabase();
00151 }
00152 
00153 bool KSycocaPrivate::openDatabase( bool openDummyIfNotFound )
00154 {
00155    bool result = true;
00156 
00157    sycoca_mmap = 0;
00158    QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00159    m_str = 0;
00160    delete m_dummyBuffer;
00161    m_dummyBuffer = 0;
00162    QString path = KSycoca::absoluteFilePath();
00163 
00164    kDebug(7011) << "Trying to open ksycoca from " << path;
00165 #ifdef Q_OS_WIN
00166    m_database = new KMemFile(path);
00167 #else
00168    m_database = new QFile(path);
00169 #endif
00170    bool bOpen = m_database->open( QIODevice::ReadOnly );
00171    if (!bOpen)
00172    {
00173      path = KSycoca::absoluteFilePath(KSycoca::GlobalDatabase);
00174      if (!path.isEmpty())
00175      {
00176        kDebug(7011) << "Trying to open global ksycoca from " << path;
00177        delete m_database;
00178 #ifdef Q_OS_WIN
00179        m_database = new KMemFile(path);
00180 #else
00181        m_database = new QFile(path);
00182 #endif
00183        bOpen = m_database->open( QIODevice::ReadOnly );
00184      }
00185    }
00186 
00187    if (bOpen)
00188    {
00189 #ifdef Q_OS_WIN
00190      m_str = new QDataStream(m_database);
00191      m_str->setVersion(QDataStream::Qt_3_1);
00192      sycoca_mmap = 0;
00193 #else // Q_OS_WIN
00194      fcntl(m_database->handle(), F_SETFD, FD_CLOEXEC);
00195      sycoca_size = m_database->size();
00196 #ifdef HAVE_MMAP
00197      sycoca_mmap = (const char *) mmap(0, sycoca_size,
00198                                        PROT_READ, MAP_SHARED,
00199                                        m_database->handle(), 0);
00200      /* POSIX mandates only MAP_FAILED, but we are paranoid so check for
00201         null pointer too.  */
00202      if (sycoca_mmap == (const char*) MAP_FAILED || sycoca_mmap == 0)
00203      {
00204         kDebug(7011) << "mmap failed. (length = " << sycoca_size << ")";
00205 #endif // HAVE_MMAP
00206         m_str = new QDataStream(m_database);
00207         m_str->setVersion(QDataStream::Qt_3_1);
00208         sycoca_mmap = 0;
00209 #ifdef HAVE_MMAP
00210      }
00211      else
00212      {
00213 #ifdef HAVE_MADVISE
00214         (void) madvise((char*)sycoca_mmap, sycoca_size, MADV_WILLNEED);
00215 #endif // HAVE_MADVISE
00216         m_dummyBuffer = new QBuffer;
00217         m_dummyBuffer->setData(QByteArray::fromRawData(sycoca_mmap, sycoca_size));
00218         m_dummyBuffer->open(QIODevice::ReadOnly);
00219         m_str = new QDataStream(m_dummyBuffer);
00220         m_str->setVersion(QDataStream::Qt_3_1);
00221      }
00222 #endif // HAVE_MMAP
00223 #endif // !Q_OS_WIN
00224      checkVersion();
00225    }
00226    else
00227    {
00228      kDebug(7011) << "Could not open ksycoca";
00229 
00230      // No database file
00231      delete m_database;
00232      m_database = 0;
00233 
00234      databaseStatus = NoDatabase;
00235      if (openDummyIfNotFound)
00236      {
00237         // We open a dummy database instead.
00238         //kDebug(7011) << "No database, opening a dummy one.";
00239         m_dummyBuffer = new QBuffer;
00240         m_dummyBuffer->open(QIODevice::ReadWrite);
00241         m_str = new QDataStream(m_dummyBuffer);
00242         m_str->setVersion(QDataStream::Qt_3_1);
00243         *m_str << qint32(KSYCOCA_VERSION);
00244         *m_str << qint32(0);
00245      }
00246      else
00247      {
00248         result = false;
00249      }
00250    }
00251    lstFactories = new KSycocaFactoryList;
00252    return result;
00253 }
00254 
00255 // Read-write constructor - only for KBuildSycoca
00256 KSycoca::KSycoca( bool /* dummy */ )
00257   : m_str(0),
00258     d(new KSycocaPrivate)
00259 {
00260    QDBusConnection::sessionBus().registerObject("/ksycoca_building", this, QDBusConnection::ExportScriptableSlots);
00261    d->lstFactories = new KSycocaFactoryList;
00262    KSycocaPrivate::_self = this;
00263 }
00264 
00265 KSycoca * KSycoca::self()
00266 {
00267     if (!KSycocaPrivate::_self) {
00268         qAddPostRoutine(KSycocaPrivate::delete_ksycoca_self);
00269         KSycocaPrivate::_self = new KSycoca;
00270     }
00271     return KSycocaPrivate::_self;
00272 }
00273 
00274 KSycoca::~KSycoca()
00275 {
00276    d->closeDatabase();
00277    delete d;
00278    KSycocaPrivate::_self = 0L;
00279 }
00280 
00281 bool KSycoca::isAvailable()
00282 {
00283     return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing/* don't open dummy db if not found */);
00284 }
00285 
00286 void KSycocaPrivate::closeDatabase()
00287 {
00288    QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00289    QIODevice *device = 0;
00290 #ifdef HAVE_MMAP
00291    if (device && sycoca_mmap)
00292    {
00293       QBuffer *buf = static_cast<QBuffer*>(device);
00294       buf->buffer().clear();
00295       // Solaris has munmap(char*, size_t) and everything else should
00296       // be happy with a char* for munmap(void*, size_t)
00297       munmap(const_cast<char*>(sycoca_mmap), sycoca_size);
00298       sycoca_mmap = 0;
00299    }
00300 #endif
00301 
00302    delete m_str;
00303    m_str = 0;
00304    delete m_dummyBuffer;
00305    m_dummyBuffer = 0;
00306    delete device;
00307    if (m_database != device)
00308       delete m_database;
00309    device = 0;
00310    m_database = 0;
00311    // It is very important to delete all factories here
00312    // since they cache information about the database file
00313    if ( lstFactories )
00314        qDeleteAll( *lstFactories );
00315    delete lstFactories;
00316    lstFactories = 0;
00317    databaseStatus = DatabaseNotOpen;
00318 }
00319 
00320 void KSycoca::addFactory( KSycocaFactory *factory )
00321 {
00322    Q_ASSERT(d->lstFactories != 0);
00323    d->lstFactories->append(factory);
00324 }
00325 
00326 bool KSycoca::isChanged(const char *type)
00327 {
00328     return self()->d->changeList.contains(type);
00329 }
00330 
00331 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00332 {
00333     d->changeList = changeList;
00334     //kDebug() << "got a notifyDatabaseChanged signal" << changeList;
00335     // kded tells us the database file changed
00336     // Close the database and forget all about what we knew
00337     // The next call to any public method will recreate
00338     // everything that's needed.
00339     d->closeDatabase();
00340 
00341     // Now notify applications
00342     emit databaseChanged();
00343 }
00344 
00345 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00346 {
00347    if ( !m_str )
00348        d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate | KSycocaPrivate::IfNotFoundOpenDummy);
00349    Q_ASSERT(m_str);
00350    //kDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16);
00351    m_str->device()->seek(offset);
00352    qint32 aType;
00353    *m_str >> aType;
00354    type = KSycocaType(aType);
00355    //kDebug(7011) << QString("KSycoca::found type %1").arg(aType);
00356    return m_str;
00357 }
00358 
00359 KSycocaFactoryList* KSycoca::factories()
00360 {
00361     return d->lstFactories;
00362 }
00363 
00364 // Warning, checkVersion rewinds to the beginning of m_str.
00365 bool KSycocaPrivate::checkVersion()
00366 {
00367     QDataStream *m_str = KSycocaPrivate::_self->m_str;
00368     Q_ASSERT(m_str);
00369     m_str->device()->seek(0);
00370     qint32 aVersion;
00371     *m_str >> aVersion;
00372     if ( aVersion < KSYCOCA_VERSION ) {
00373         kWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher.";
00374         databaseStatus = BadVersion;
00375         return false;
00376     } else {
00377         databaseStatus = DatabaseOK;
00378         return true;
00379     }
00380 }
00381 
00382 // If it returns true, we have a valid database and the stream has rewinded to the beginning
00383 // and past the version number.
00384 bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound)
00385 {
00386     QDataStream* &m_str = KSycocaPrivate::_self->m_str;
00387     if (databaseStatus == DatabaseOK) {
00388         Q_ASSERT(m_str);
00389         if (checkVersion()) // we know the version is ok, but we must rewind the stream anyway
00390             return true;
00391     }
00392 
00393     closeDatabase(); // close the dummy one
00394     // Check if new database already available
00395     if( openDatabase(ifNotFound & IfNotFoundOpenDummy) ) {
00396         Q_ASSERT(m_str); // if a database was found then m_str shouldn't be 0
00397         if (checkVersion()) {
00398             // Database exists, and version is ok.
00399             return true;
00400         }
00401     }
00402 
00403     static bool triedLaunchingKdeinit = false;
00404     if ((ifNotFound & IfNotFoundRecreate) && !triedLaunchingKdeinit) { // try only once
00405         triedLaunchingKdeinit = true;
00406         // Well, if kdeinit is not running we need to launch it,
00407         // but otherwise we simply need to run kbuildsycoca to recreate the sycoca file.
00408         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.klauncher")) {
00409             kDebug(7011) << "We have no database.... launching kdeinit";
00410             KToolInvocation::klauncher(); // this calls startKdeinit
00411         } else {
00412             kDebug(7011) << "We have no database.... launching " << KBUILDSYCOCA_EXENAME;
00413             if (QProcess::execute(KStandardDirs::findExe(KBUILDSYCOCA_EXENAME)) != 0)
00414                 qWarning("ERROR: Running KSycoca failed.");
00415         }
00416 
00417         // Wait until the DBUS signal from kbuildsycoca
00418         QEventLoop eventLoop;
00419         QObject::connect(KSycoca::self(), SIGNAL(databaseChanged()), &eventLoop, SLOT(quit()));
00420         eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
00421 
00422         // Ok, the new database should be here now, open it.
00423         if (!openDatabase(ifNotFound & IfNotFoundOpenDummy)) {
00424             kDebug(7011) << "Still no database...";
00425             return false; // Still no database - uh oh
00426         }
00427         if (!checkVersion()) {
00428             kDebug(7011) << "Still outdated...";
00429             return false; // Still outdated - uh oh
00430         }
00431         return true;
00432     }
00433 
00434     return false;
00435 }
00436 
00437 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00438 {
00439     // Ensure we have a valid database (right version, and rewinded to beginning)
00440     if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
00441         return 0;
00442     }
00443 
00444     qint32 aId;
00445     qint32 aOffset;
00446     while(true) {
00447         *m_str >> aId;
00448         if (aId == 0) {
00449             kError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00450             break;
00451         }
00452         *m_str >> aOffset;
00453         if (aId == id) {
00454             //kDebug(7011) << "KSycoca::findFactory(" << id << ") offset " << aOffset;
00455             m_str->device()->seek(aOffset);
00456             return m_str;
00457         }
00458     }
00459     return 0;
00460 }
00461 
00462 QString KSycoca::kfsstnd_prefixes()
00463 {
00464     // do not try to launch kbuildsycoca from here; this code is also called by kbuildsycoca.
00465    if (!d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) return "";
00466    qint32 aId;
00467    qint32 aOffset;
00468    // skip factories offsets
00469    while(true)
00470    {
00471       *m_str >> aId;
00472       if ( aId )
00473         *m_str >> aOffset;
00474       else
00475         break; // just read 0
00476    }
00477    // We now point to the header
00478    QString prefixes;
00479    KSycocaEntry::read(*m_str, prefixes);
00480    *m_str >> d->timeStamp;
00481    KSycocaEntry::read(*m_str, d->language);
00482    *m_str >> d->updateSig;
00483    KSycocaEntry::read(*m_str, d->allResourceDirs);
00484    return prefixes;
00485 }
00486 
00487 quint32 KSycoca::timeStamp()
00488 {
00489    if (!d->timeStamp)
00490       (void) kfsstnd_prefixes();
00491    return d->timeStamp;
00492 }
00493 
00494 quint32 KSycoca::updateSignature()
00495 {
00496    if (!d->timeStamp)
00497       (void) kfsstnd_prefixes();
00498    return d->updateSig;
00499 }
00500 
00501 QString KSycoca::absoluteFilePath(DatabaseType type)
00502 {
00503    if (type == GlobalDatabase)
00504       return KStandardDirs::locate("services", KSYCOCA_FILENAME);
00505 
00506    const QByteArray ksycoca_env = qgetenv("KDESYCOCA");
00507    if (ksycoca_env.isEmpty())
00508       return KGlobal::dirs()->saveLocation("cache") + KSYCOCA_FILENAME;
00509    else
00510       return QFile::decodeName(ksycoca_env);
00511 }
00512 
00513 QString KSycoca::language()
00514 {
00515    if (d->language.isEmpty())
00516       (void) kfsstnd_prefixes();
00517    return d->language;
00518 }
00519 
00520 QStringList KSycoca::allResourceDirs()
00521 {
00522    if (!d->timeStamp)
00523       (void) kfsstnd_prefixes();
00524    return d->allResourceDirs;
00525 }
00526 
00527 #if 0
00528 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00529 {
00530   QString sRelativeFilePath;
00531   QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00532   QStringList::ConstIterator dirsit = dirs.begin();
00533   for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00534     // might need canonicalPath() ...
00535     if ( _fullpath.indexOf( *dirsit ) == 0 ) // path is dirs + relativePath
00536       sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs
00537   }
00538   if ( sRelativeFilePath.isEmpty() )
00539     kFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource);
00540   //else
00541     // debug code
00542     //kDebug(7011) << sRelativeFilePath;
00543   return sRelativeFilePath;
00544 }
00545 #endif
00546 
00547 void KSycoca::flagError()
00548 {
00549     kWarning(7011) << "ERROR: KSycoca database corruption!";
00550    if (KSycocaPrivate::_self)
00551    {
00552       if (KSycocaPrivate::_self->d->readError)
00553          return;
00554       KSycocaPrivate::_self->d->readError = true;
00555       if (KSycocaPrivate::_self->d->autoRebuild) {
00556           // Rebuild the damned thing.
00557           if (QProcess::execute(KStandardDirs::findExe(KBUILDSYCOCA_EXENAME)) != 0)
00558               qWarning("ERROR: Running %s failed", KBUILDSYCOCA_EXENAME);
00559           // Do not wait until the DBUS signal from kbuildsycoca here.
00560           // It deletes m_str which is a problem when flagError is called during the KSycocaFactory ctor...
00561       }
00562    }
00563 }
00564 
00565 bool KSycoca::isBuilding()
00566 {
00567     return false;
00568 }
00569 
00570 void KSycoca::disableAutoRebuild()
00571 {
00572    d->autoRebuild = false;
00573 }
00574 
00575 bool KSycoca::readError()
00576 {
00577    bool b = false;
00578    if (KSycocaPrivate::_self)
00579    {
00580       b = KSycocaPrivate::_self->d->readError;
00581       KSycocaPrivate::_self->d->readError = false;
00582    }
00583    return b;
00584 }
00585 
00586 #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
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • 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