• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • 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 "config.h"
00020 
00021 #include "ksycoca.h"
00022 #include "ksycocatype.h"
00023 #include "ksycocafactory.h"
00024 
00025 #include <qdatastream.h>
00026 #include <qfile.h>
00027 #include <qbuffer.h>
00028 
00029 #include <kapplication.h>
00030 #include <dcopclient.h>
00031 #include <kglobal.h>
00032 #include <kdebug.h>
00033 #include <kprocess.h>
00034 #include <kstandarddirs.h>
00035 
00036 #include <assert.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <fcntl.h>
00040               
00041 #ifdef HAVE_SYS_MMAN_H
00042 #include <sys/mman.h>
00043 #endif
00044 
00045 #ifdef Q_OS_SOLARIS
00046 extern "C" 
00047 {
00048     extern int madvise(caddr_t, size_t, int); 
00049 }
00050 #endif
00051 
00052 #ifndef MAP_FAILED
00053 #define MAP_FAILED ((void *) -1)
00054 #endif
00055 
00056 template class QPtrList<KSycocaFactory>;
00057 
00058 // The following limitations are in place:
00059 // Maximum length of a single string: 8192 bytes
00060 // Maximum length of a string list: 1024 strings
00061 // Maximum number of entries: 8192
00062 //
00063 // The purpose of these limitations is to limit the impact
00064 // of database corruption.
00065 
00066 class KSycocaPrivate {
00067 public:
00068     KSycocaPrivate() {
00069         database = 0;
00070         readError = false;
00071         updateSig = 0;
00072         autoRebuild = true;
00073     }
00074     QFile *database;
00075     QStringList changeList;
00076     QString language;
00077     bool readError;
00078     bool autoRebuild;
00079     Q_UINT32 updateSig;
00080     QStringList allResourceDirs;
00081 };
00082 
00083 int KSycoca::version()
00084 {
00085    return KSYCOCA_VERSION;
00086 }
00087 
00088 // Read-only constructor
00089 KSycoca::KSycoca()
00090   : DCOPObject("ksycoca"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00091     m_sycoca_size(0), m_sycoca_mmap(0), m_timeStamp(0)
00092 {
00093    d = new KSycocaPrivate;
00094    // Register app as able to receive DCOP messages
00095    if (kapp && !kapp->dcopClient()->isAttached())
00096    {
00097       kapp->dcopClient()->attach();
00098    }
00099    // We register with DCOP _before_ we try to open the database.
00100    // This way we can be relative sure that the KDE framework is
00101    // up and running (kdeinit, dcopserver, klaucnher, kded) and
00102    // that the database is up to date.
00103    openDatabase();
00104    _self = this;
00105 }
00106 
00107 bool KSycoca::openDatabase( bool openDummyIfNotFound )
00108 {
00109    bool result = true;
00110   
00111    m_sycoca_mmap = 0;
00112    m_str = 0;
00113    QString path;
00114    QCString ksycoca_env = getenv("KDESYCOCA");
00115    if (ksycoca_env.isEmpty())
00116       path = KGlobal::dirs()->saveLocation("cache") + "ksycoca";
00117    else
00118       path = QFile::decodeName(ksycoca_env);
00119 
00120    kdDebug(7011) << "Trying to open ksycoca from " << path << endl;
00121    QFile *database = new QFile(path);
00122    bool bOpen = database->open( IO_ReadOnly );
00123    if (!bOpen)
00124    {
00125      path = locate("services", "ksycoca");
00126      if (!path.isEmpty())
00127      {
00128        kdDebug(7011) << "Trying to open global ksycoca from " << path << endl;
00129        delete database;
00130        database = new QFile(path);
00131        bOpen = database->open( IO_ReadOnly );
00132      }
00133    }
00134    
00135    if (bOpen)
00136    {
00137      fcntl(database->handle(), F_SETFD, FD_CLOEXEC);
00138      m_sycoca_size = database->size();
00139 #ifdef HAVE_MMAP
00140      m_sycoca_mmap = (const char *) mmap(0, m_sycoca_size,
00141                                 PROT_READ, MAP_SHARED,
00142                                 database->handle(), 0);
00143      /* POSIX mandates only MAP_FAILED, but we are paranoid so check for
00144         null pointer too.  */
00145      if (m_sycoca_mmap == (const char*) MAP_FAILED || m_sycoca_mmap == 0)
00146      {
00147         kdDebug(7011) << "mmap failed. (length = " << m_sycoca_size << ")" << endl;
00148 #endif
00149         m_str = new QDataStream(database);
00150 #ifdef HAVE_MMAP
00151      }
00152      else
00153      {
00154 #ifdef HAVE_MADVISE
00155     (void) madvise((char*)m_sycoca_mmap, m_sycoca_size, MADV_WILLNEED);
00156 #endif
00157         QByteArray b_array;
00158         b_array.setRawData(m_sycoca_mmap, m_sycoca_size);
00159         QBuffer *buffer = new QBuffer( b_array );
00160         buffer->open(IO_ReadWrite);
00161         m_str = new QDataStream( buffer);
00162      }
00163 #endif
00164      bNoDatabase = false;
00165    }
00166    else
00167    {
00168      kdDebug(7011) << "Could not open ksycoca" << endl;
00169 
00170      // No database file
00171      delete database;
00172      database = 0;
00173 
00174      bNoDatabase = true;
00175      if (openDummyIfNotFound)
00176      {
00177         // We open a dummy database instead.
00178         //kdDebug(7011) << "No database, opening a dummy one." << endl;
00179         QBuffer *buffer = new QBuffer( QByteArray() );
00180         buffer->open(IO_ReadWrite);
00181         m_str = new QDataStream( buffer);
00182         (*m_str) << (Q_INT32) KSYCOCA_VERSION;
00183         (*m_str) << (Q_INT32) 0;
00184      }
00185      else
00186      {
00187         result = false;
00188      }
00189    }
00190    m_lstFactories = new KSycocaFactoryList();
00191    m_lstFactories->setAutoDelete( true );
00192    d->database = database;
00193    return result;
00194 }
00195 
00196 // Read-write constructor - only for KBuildSycoca
00197 KSycoca::KSycoca( bool /* dummy */ )
00198   : DCOPObject("ksycoca_building"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00199     m_sycoca_size(0), m_sycoca_mmap(0)
00200 {
00201    d = new KSycocaPrivate;
00202    m_lstFactories = new KSycocaFactoryList();
00203    m_lstFactories->setAutoDelete( true );
00204    _self = this;
00205 }
00206 
00207 static void delete_ksycoca_self() {
00208   delete KSycoca::_self;
00209 }
00210 
00211 KSycoca * KSycoca::self()
00212 {
00213     if (!_self) {
00214         qAddPostRoutine(delete_ksycoca_self);
00215         _self = new KSycoca();
00216     }
00217   return _self;
00218 }
00219 
00220 KSycoca::~KSycoca()
00221 {
00222    closeDatabase();
00223    delete d;
00224    _self = 0L;
00225 }
00226 
00227 void KSycoca::closeDatabase()
00228 {
00229    QIODevice *device = 0;
00230    if (m_str)
00231       device = m_str->device();
00232 #ifdef HAVE_MMAP
00233    if (device && m_sycoca_mmap)
00234    {
00235       QBuffer *buf = (QBuffer *) device;
00236       buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00237       // Solaris has munmap(char*, size_t) and everything else should
00238       // be happy with a char* for munmap(void*, size_t)
00239       munmap((char*) m_sycoca_mmap, m_sycoca_size);
00240       m_sycoca_mmap = 0;
00241    }
00242 #endif
00243 
00244    delete m_str;
00245    m_str = 0;
00246    delete device;
00247    if (d->database != device)
00248       delete d->database;
00249    device = 0;
00250    d->database = 0;
00251    // It is very important to delete all factories here
00252    // since they cache information about the database file
00253    delete m_lstFactories;
00254    m_lstFactories = 0L;
00255 }
00256 
00257 void KSycoca::addFactory( KSycocaFactory *factory )
00258 {
00259    assert(m_lstFactories);
00260    m_lstFactories->append(factory);
00261 }
00262 
00263 bool KSycoca::isChanged(const char *type)
00264 {
00265     return self()->d->changeList.contains(type);
00266 }
00267 
00268 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00269 {
00270     d->changeList = changeList;
00271     //kdDebug(7011) << "got a notifyDatabaseChanged signal !" << endl;
00272     // kded tells us the database file changed
00273     // Close the database and forget all about what we knew
00274     // The next call to any public method will recreate
00275     // everything that's needed.
00276     closeDatabase();
00277 
00278     // Now notify applications
00279     emit databaseChanged();
00280 }
00281 
00282 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00283 {
00284    if ( !m_str )
00285       openDatabase();
00286    //kdDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16) << endl;
00287    m_str->device()->at(offset);
00288    Q_INT32 aType;
00289    (*m_str) >> aType;
00290    type = (KSycocaType) aType;
00291    //kdDebug(7011) << QString("KSycoca::found type %1").arg(aType) << endl;
00292    return m_str;
00293 }
00294 
00295 bool KSycoca::checkVersion(bool abortOnError)
00296 {
00297    if ( !m_str )
00298    {
00299       if( !openDatabase(false /* don't open dummy db if not found */) )
00300         return false; // No database found
00301 
00302       // We should never get here... if a database was found then m_str shouldn't be 0L.
00303       assert(m_str);
00304    }
00305    m_str->device()->at(0);
00306    Q_INT32 aVersion;
00307    (*m_str) >> aVersion;
00308    if ( aVersion < KSYCOCA_VERSION )
00309    {
00310       kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl;
00311       if (!abortOnError) return false;
00312       kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl;
00313       abort();
00314    }
00315    return true;
00316 }
00317 
00318 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00319 {
00320    // The constructor found no database, but we want one
00321    if (bNoDatabase)
00322    {
00323       closeDatabase(); // close the dummy one
00324       // Check if new database already available
00325       if ( !openDatabase(false /* no dummy one*/) )
00326       {
00327          static bool triedLaunchingKdeinit = false;
00328          if (!triedLaunchingKdeinit) // try only once
00329          {
00330            triedLaunchingKdeinit = true;
00331            kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl;
00332            KApplication::startKdeinit();
00333            // Ok, the new database should be here now, open it.
00334          }
00335          if (!openDatabase(false))
00336             return 0L; // Still no database - uh oh
00337       }
00338    }
00339    // rewind and check
00340    if (!checkVersion(false))
00341    {
00342      kdWarning(7011) << "Outdated database found" << endl;
00343      return 0L;
00344    }
00345    Q_INT32 aId;
00346    Q_INT32 aOffset;
00347    while(true)
00348    {
00349       (*m_str) >> aId;
00350       //kdDebug(7011) << QString("KSycoca::findFactory : found factory %1").arg(aId) << endl;
00351       if (aId == 0)
00352       {
00353          kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00354          break;
00355       }
00356       (*m_str) >> aOffset;
00357       if (aId == id)
00358       {
00359          //kdDebug(7011) << QString("KSycoca::findFactory(%1) offset %2").arg((int)id).arg(aOffset) << endl;
00360          m_str->device()->at(aOffset);
00361          return m_str;
00362       }
00363    }
00364    return 0;
00365 }
00366 
00367 QString KSycoca::kfsstnd_prefixes()
00368 {
00369    if (bNoDatabase) return "";
00370    if (!checkVersion(false)) return "";
00371    Q_INT32 aId;
00372    Q_INT32 aOffset;
00373    // skip factories offsets
00374    while(true)
00375    {
00376       (*m_str) >> aId;
00377       if ( aId )
00378         (*m_str) >> aOffset;
00379       else
00380         break; // just read 0
00381    }
00382    // We now point to the header
00383    QString prefixes;
00384    KSycocaEntry::read(*m_str, prefixes);
00385    (*m_str) >> m_timeStamp;
00386    KSycocaEntry::read(*m_str, d->language);
00387    (*m_str) >> d->updateSig;
00388    KSycocaEntry::read(*m_str, d->allResourceDirs);
00389    return prefixes;
00390 }
00391 
00392 Q_UINT32 KSycoca::timeStamp()
00393 {
00394    if (!m_timeStamp)
00395       (void) kfsstnd_prefixes();
00396    return m_timeStamp;
00397 }
00398 
00399 Q_UINT32 KSycoca::updateSignature()
00400 {
00401    if (!m_timeStamp)
00402       (void) kfsstnd_prefixes();
00403    return d->updateSig;
00404 }
00405 
00406 QString KSycoca::language()
00407 {
00408    if (d->language.isEmpty())
00409       (void) kfsstnd_prefixes();
00410    return d->language;
00411 }
00412 
00413 QStringList KSycoca::allResourceDirs()
00414 {
00415    if (!m_timeStamp)
00416       (void) kfsstnd_prefixes();
00417    return d->allResourceDirs;
00418 }
00419 
00420 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00421 {
00422   QString sRelativeFilePath;
00423   QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00424   QStringList::ConstIterator dirsit = dirs.begin();
00425   for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00426     // might need canonicalPath() ...
00427     if ( _fullpath.find( *dirsit ) == 0 ) // path is dirs + relativePath
00428       sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs
00429   }
00430   if ( sRelativeFilePath.isEmpty() )
00431     kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl;
00432   //else
00433     // debug code
00434     //kdDebug(7011) << sRelativeFilePath << endl;
00435   return sRelativeFilePath;
00436 }
00437 
00438 KSycoca * KSycoca::_self = 0L;
00439 
00440 void KSycoca::flagError()
00441 {
00442    qWarning("ERROR: KSycoca database corruption!");
00443    if (_self)
00444    {
00445       if (_self->d->readError)
00446          return;
00447       _self->d->readError = true;
00448       if (_self->d->autoRebuild)
00449          if(system("kbuildsycoca") < 0) // Rebuild the damned thing.
00450        qWarning("ERROR: Running KSycoca failed.");
00451    }
00452 }
00453 
00454 void KSycoca::disableAutoRebuild()
00455 {
00456    d->autoRebuild = false;
00457 }
00458 
00459 bool KSycoca::readError()
00460 {
00461    bool b = false;
00462    if (_self)
00463    {
00464       b = _self->d->readError;
00465       _self->d->readError = false;
00466    }
00467    return b;
00468 }
00469 
00470 void KSycocaEntry::read( QDataStream &s, QString &str )
00471 {
00472   Q_UINT32 bytes;
00473   s >> bytes;                          // read size of string
00474   if ( bytes > 8192 ) {                // null string or too big
00475       if (bytes != 0xffffffff)
00476          KSycoca::flagError();
00477       str = QString::null;
00478   } 
00479   else if ( bytes > 0 ) {              // not empty
00480       int bt = bytes/2;
00481       str.setLength( bt );
00482       QChar* ch = (QChar *) str.unicode();
00483       char t[8192];
00484       char *b = t;
00485       s.readRawBytes( b, bytes );
00486       while ( bt-- ) {
00487           *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00488       b += 2;
00489       }
00490   } else {
00491       str = "";
00492   }
00493 }
00494 
00495 void KSycocaEntry::read( QDataStream &s, QStringList &list )
00496 {
00497   list.clear();
00498   Q_UINT32 count;
00499   s >> count;                          // read size of list
00500   if (count >= 1024)
00501   {
00502      KSycoca::flagError();
00503      return;
00504   }
00505   for(Q_UINT32 i = 0; i < count; i++)
00506   {
00507      QString str;
00508      read(s, str);
00509      list.append( str );
00510      if (s.atEnd())
00511      {
00512         KSycoca::flagError();
00513         return;
00514      }
00515   }
00516 }
00517 
00518 void KSycoca::virtual_hook( int id, void* data )
00519 { DCOPObject::virtual_hook( id, data ); }
00520 
00521 void KSycocaEntry::virtual_hook( int, void* )
00522 { /*BASE::virtual_hook( id, data );*/ }
00523 
00524 #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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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