KDED
kbuildsycoca.cpp
Go to the documentation of this file.
00001 // -*- c-basic-offset: 3 -*- 00002 /* This file is part of the KDE libraries 00003 * Copyright (C) 1999 David Faure <faure@kde.org> 00004 * Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation; 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 **/ 00020 00021 #include "kbuildsycoca.h" 00022 #include "ksycoca_p.h" 00023 #include "kresourcelist.h" 00024 #include "vfolder_menu.h" 00025 00026 #include <config.h> 00027 #include <config-kded.h> 00028 00029 #include <kservice.h> 00030 #include <kmimetype.h> 00031 #include "kbuildservicetypefactory.h" 00032 #include "kbuildmimetypefactory.h" 00033 #include "kbuildservicefactory.h" 00034 #include "kbuildservicegroupfactory.h" 00035 #include "kbuildprotocolinfofactory.h" 00036 #include "kctimefactory.h" 00037 #include <ktemporaryfile.h> 00038 #include <QtCore/QDataStream> 00039 #include <QtCore/QDir> 00040 #include <QtCore/QEventLoop> 00041 #include <QtCore/QFile> 00042 #include <QtCore/QTimer> 00043 #include <QtDBus/QtDBus> 00044 #include <errno.h> 00045 00046 #include <assert.h> 00047 #include <kapplication.h> 00048 #include <kglobal.h> 00049 #include <kdebug.h> 00050 #include <kdirwatch.h> 00051 #include <kstandarddirs.h> 00052 #include <ksavefile.h> 00053 #include <klocale.h> 00054 #include <kaboutdata.h> 00055 #include <kcmdlineargs.h> 00056 #ifndef KBUILDSYCOCA_NO_KCRASH 00057 #include <kcrash.h> 00058 #endif 00059 #include <kmemfile.h> 00060 00061 #include <stdlib.h> 00062 #include <unistd.h> 00063 #include <time.h> 00064 #include <memory> // auto_ptr 00065 00066 typedef QHash<QString, KSycocaEntry::Ptr> KBSEntryDict; 00067 typedef QList<KSycocaEntry::List> KSycocaEntryListList; 00068 00069 static quint32 newTimestamp = 0; 00070 00071 static KBuildServiceFactory *g_serviceFactory = 0; 00072 static KBuildServiceGroupFactory *g_buildServiceGroupFactory = 0; 00073 static KSycocaFactory *g_currentFactory = 0; 00074 static KCTimeInfo *g_ctimeInfo = 0; // factory 00075 static KCTimeDict *g_ctimeDict = 0; // old timestamps 00076 static QByteArray g_resource = 0; 00077 static KBSEntryDict *g_currentEntryDict = 0; 00078 static KBSEntryDict *g_serviceGroupEntryDict = 0; 00079 static KSycocaEntryListList *g_allEntries = 0; // entries from existing ksycoca 00080 static QStringList *g_allResourceDirs = 0; 00081 static bool g_changed = false; 00082 static KSycocaEntry::List g_tempStorage; 00083 static VFolderMenu *g_vfolder = 0; 00084 00085 static const char *cSycocaPath = 0; 00086 00087 static bool bGlobalDatabase = false; 00088 static bool bMenuTest = false; 00089 00090 void crashHandler(int) 00091 { 00092 // If we crash while reading sycoca, we delete the database 00093 // in an attempt to recover. 00094 if (cSycocaPath) 00095 unlink(cSycocaPath); 00096 } 00097 00098 static QString sycocaPath() 00099 { 00100 return KSycoca::absoluteFilePath(bGlobalDatabase ? KSycoca::GlobalDatabase : KSycoca::LocalDatabase); 00101 } 00102 00103 KBuildSycoca::KBuildSycoca() 00104 : KSycoca( true ) 00105 { 00106 } 00107 00108 KBuildSycoca::~KBuildSycoca() 00109 { 00110 00111 } 00112 00113 KSycocaEntry::Ptr KBuildSycoca::createEntry(const QString &file, bool addToFactory) 00114 { 00115 quint32 timeStamp = g_ctimeInfo->dict()->ctime(file, g_resource); 00116 if (!timeStamp) 00117 { 00118 timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file, 00119 KStandardDirs::Recursive); 00120 } 00121 KSycocaEntry::Ptr entry; 00122 if (g_allEntries) 00123 { 00124 assert(g_ctimeDict); 00125 quint32 oldTimestamp = g_ctimeDict->ctime(file, g_resource); 00126 00127 if (timeStamp && (timeStamp == oldTimestamp)) 00128 { 00129 // Re-use old entry 00130 if (g_currentFactory == g_buildServiceGroupFactory) // Strip .directory from service-group entries 00131 { 00132 entry = g_currentEntryDict->value(file.left(file.length()-10)); 00133 } else { 00134 entry = g_currentEntryDict->value(file); 00135 } 00136 // remove from g_ctimeDict; if g_ctimeDict is not empty 00137 // after all files have been processed, it means 00138 // some files were removed since last time 00139 if (file.contains("fake")) 00140 kDebug(7021) << g_resource << "reusing (and removing) old entry [\"fake\"] for:" << file; 00141 g_ctimeDict->remove(file, g_resource); 00142 } 00143 else if (oldTimestamp) 00144 { 00145 g_changed = true; 00146 g_ctimeDict->remove(file, g_resource); 00147 kDebug(7021) << "modified:" << file << "in" << g_resource.constData(); 00148 } 00149 else 00150 { 00151 g_changed = true; 00152 kDebug(7021) << "new:" << file << "in" << g_resource.constData(); 00153 } 00154 } 00155 g_ctimeInfo->dict()->addCTime(file, g_resource, timeStamp); 00156 if (!entry) 00157 { 00158 // Create a new entry 00159 entry = g_currentFactory->createEntry( file, g_resource ); 00160 } 00161 if ( entry && entry->isValid() ) 00162 { 00163 if (addToFactory) 00164 g_currentFactory->addEntry(entry); 00165 else 00166 g_tempStorage.append(entry); 00167 return entry; 00168 } 00169 return KSycocaEntry::Ptr(); 00170 } 00171 00172 KService::Ptr KBuildSycoca::createService(const QString &path) 00173 { 00174 KSycocaEntry::Ptr entry = createEntry(path, false); 00175 return KService::Ptr::staticCast(entry); 00176 } 00177 00178 // returns false if the database is up to date, true if it needs to be saved 00179 bool KBuildSycoca::build() 00180 { 00181 typedef QLinkedList<KBSEntryDict *> KBSEntryDictList; 00182 KBSEntryDictList entryDictList; 00183 KBSEntryDict *serviceEntryDict = 0; 00184 00185 // Convert for each factory the entryList to a Dict. 00186 int i = 0; 00187 // For each factory 00188 for (KSycocaFactoryList::Iterator factory = factories()->begin(); 00189 factory != factories()->end(); 00190 ++factory) 00191 { 00192 KBSEntryDict *entryDict = new KBSEntryDict; 00193 if (g_allEntries) 00194 { 00195 const KSycocaEntry::List list = (*g_allEntries)[i++]; 00196 for( KSycocaEntry::List::const_iterator it = list.begin(); 00197 it != list.end(); 00198 ++it) 00199 { 00200 entryDict->insert( (*it)->entryPath(), *it ); 00201 } 00202 } 00203 if ((*factory) == g_serviceFactory) 00204 serviceEntryDict = entryDict; 00205 else if ((*factory) == g_buildServiceGroupFactory) 00206 g_serviceGroupEntryDict = entryDict; 00207 entryDictList.append(entryDict); 00208 } 00209 00210 QStringList allResources; // we could use QSet<QString> - does order matter? 00211 // For each factory 00212 for (KSycocaFactoryList::Iterator factory = factories()->begin(); 00213 factory != factories()->end(); 00214 ++factory) 00215 { 00216 // For each resource the factory deals with 00217 const KSycocaResourceList *list = (*factory)->resourceList(); 00218 if (!list) continue; 00219 00220 for( KSycocaResourceList::ConstIterator it1 = list->constBegin(); 00221 it1 != list->constEnd(); 00222 ++it1 ) 00223 { 00224 KSycocaResource res = (*it1); 00225 if (!allResources.contains(res.resource)) 00226 allResources.append(res.resource); 00227 } 00228 } 00229 00230 g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!! 00231 bool uptodate = true; 00232 // For all resources 00233 for( QStringList::ConstIterator it1 = allResources.constBegin(); 00234 it1 != allResources.constEnd(); 00235 ++it1 ) 00236 { 00237 g_changed = false; 00238 g_resource = (*it1).toLatin1(); 00239 00240 QStringList relFiles; 00241 00242 (void) KGlobal::dirs()->findAllResources( g_resource, 00243 QString(), 00244 KStandardDirs::Recursive | 00245 KStandardDirs::NoDuplicates, 00246 relFiles); 00247 00248 00249 // Now find all factories that use this resource.... 00250 // For each factory 00251 KBSEntryDictList::const_iterator ed_it = entryDictList.begin(); 00252 const KBSEntryDictList::const_iterator ed_end = entryDictList.end(); 00253 KSycocaFactoryList::const_iterator it = factories()->constBegin(); 00254 const KSycocaFactoryList::const_iterator end = factories()->constEnd(); 00255 for ( ; it != end; ++it, ++ed_it ) 00256 { 00257 g_currentFactory = (*it); 00258 // g_ctimeInfo gets created after the initial loop, so it has no entryDict. 00259 g_currentEntryDict = ed_it == ed_end ? 0 : *ed_it; 00260 // For each resource the factory deals with 00261 const KSycocaResourceList *list = g_currentFactory->resourceList(); 00262 if (!list) continue; 00263 00264 for( KSycocaResourceList::ConstIterator it2 = list->constBegin(); 00265 it2 != list->constEnd(); 00266 ++it2 ) 00267 { 00268 KSycocaResource res = (*it2); 00269 if (res.resource != (*it1)) continue; 00270 00271 // For each file in the resource 00272 for( QStringList::ConstIterator it3 = relFiles.constBegin(); 00273 it3 != relFiles.constEnd(); 00274 ++it3 ) 00275 { 00276 // Check if file matches filter 00277 if ((*it3).endsWith(res.extension)) 00278 createEntry(*it3, true); 00279 } 00280 } 00281 } 00282 if (g_changed || !g_allEntries) 00283 { 00284 uptodate = false; 00285 //kDebug() << "CHANGED:" << g_resource; 00286 m_changedResources.append(g_resource); 00287 } 00288 } 00289 00290 bool result = !uptodate || (g_ctimeDict && !g_ctimeDict->isEmpty()); 00291 if (g_ctimeDict && !g_ctimeDict->isEmpty()) { 00292 //kDebug() << "Still in time dict:"; 00293 //g_ctimeDict->dump(); 00294 // ## It seems entries filtered out by vfolder are still in there, 00295 // so we end up always saving ksycoca, i.e. this method never returns false 00296 00297 // Get the list of resources from which some files were deleted 00298 const QStringList resources = g_ctimeDict->resourceList(); 00299 kDebug() << "Still in the time dict (i.e. deleted files)" << resources; 00300 m_changedResources += resources; 00301 } 00302 00303 if (result || bMenuTest) 00304 { 00305 g_resource = "apps"; 00306 g_currentFactory = g_serviceFactory; 00307 g_currentEntryDict = serviceEntryDict; 00308 g_changed = false; 00309 00310 g_vfolder = new VFolderMenu(g_serviceFactory, this); 00311 if (!m_trackId.isEmpty()) 00312 g_vfolder->setTrackId(m_trackId); 00313 00314 VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu", true); 00315 00316 KServiceGroup::Ptr entry = g_buildServiceGroupFactory->addNew("/", kdeMenu->directoryFile, KServiceGroup::Ptr(), false); 00317 entry->setLayoutInfo(kdeMenu->layoutList); 00318 createMenu(QString(), QString(), kdeMenu); 00319 00320 (void) existingResourceDirs(); 00321 *g_allResourceDirs += g_vfolder->allDirectories(); 00322 00323 if (g_changed || !g_allEntries) 00324 { 00325 uptodate = false; 00326 //kDebug() << "CHANGED:" << g_resource; 00327 m_changedResources.append(g_resource); 00328 } 00329 if (bMenuTest) { 00330 result = false; 00331 } 00332 } 00333 00334 qDeleteAll(entryDictList); 00335 return result; 00336 } 00337 00338 void KBuildSycoca::createMenu(const QString &caption_, const QString &name_, VFolderMenu::SubMenu *menu) 00339 { 00340 QString caption = caption_; 00341 QString name = name_; 00342 foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus) 00343 { 00344 QString subName = name+subMenu->name+'/'; 00345 00346 QString directoryFile = subMenu->directoryFile; 00347 if (directoryFile.isEmpty()) 00348 directoryFile = subName+".directory"; 00349 quint32 timeStamp = g_ctimeInfo->dict()->ctime(directoryFile, g_resource); 00350 if (!timeStamp) { 00351 timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile, 00352 KStandardDirs::Recursive ); 00353 } 00354 00355 KServiceGroup::Ptr entry; 00356 if (g_allEntries) 00357 { 00358 const quint32 oldTimestamp = g_ctimeDict->ctime(directoryFile, g_resource); 00359 00360 if (timeStamp && (timeStamp == oldTimestamp)) 00361 { 00362 KSycocaEntry::Ptr group = g_serviceGroupEntryDict->value(subName); 00363 if ( group ) 00364 { 00365 entry = KServiceGroup::Ptr::staticCast( group ); 00366 if (entry->directoryEntryPath() != directoryFile) 00367 entry = 0; // Can't reuse this one! 00368 } 00369 } 00370 } 00371 g_ctimeInfo->dict()->addCTime(directoryFile, g_resource, timeStamp); 00372 00373 entry = g_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted); 00374 entry->setLayoutInfo(subMenu->layoutList); 00375 if (! (bMenuTest && entry->noDisplay()) ) 00376 createMenu(caption + entry->caption() + '/', subName, subMenu); 00377 } 00378 if (caption.isEmpty()) 00379 caption += '/'; 00380 if (name.isEmpty()) 00381 name += '/'; 00382 foreach (const KService::Ptr &p, menu->items) 00383 { 00384 if (bMenuTest) 00385 { 00386 if (!menu->isDeleted && !p->noDisplay()) 00387 printf("%s\t%s\t%s\n", qPrintable( caption ), qPrintable( p->menuId() ), qPrintable( KStandardDirs::locate("apps", p->entryPath() ) ) ); 00388 } 00389 else 00390 { 00391 g_buildServiceGroupFactory->addNewEntryTo( name, p ); 00392 } 00393 } 00394 } 00395 00396 bool KBuildSycoca::recreate() 00397 { 00398 QString path(sycocaPath()); 00399 00400 // KSaveFile first writes to a temp file. 00401 // Upon finalize() it moves the stuff to the right place. 00402 KSaveFile database(path); 00403 bool openedOK = database.open(); 00404 if (!openedOK && database.error() == QFile::PermissionsError && QFile::exists(path)) 00405 { 00406 QFile::remove( path ); 00407 openedOK = database.open(); 00408 } 00409 if (!openedOK) 00410 { 00411 fprintf(stderr, "kbuildsycoca4: ERROR creating database '%s'! %s\n", 00412 path.toLocal8Bit().data(), database.errorString().toLocal8Bit().data()); 00413 return false; 00414 } 00415 00416 QDataStream* str = new QDataStream(&database); 00417 str->setVersion(QDataStream::Qt_3_1); 00418 00419 kDebug(7021).nospace() << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")"; 00420 00421 // It is very important to build the servicetype one first 00422 // Both are registered in KSycoca, no need to keep the pointers 00423 KSycocaFactory *stf = new KBuildServiceTypeFactory; 00424 KBuildMimeTypeFactory* mimeTypeFactory = new KBuildMimeTypeFactory; 00425 g_buildServiceGroupFactory = new KBuildServiceGroupFactory(); 00426 g_serviceFactory = new KBuildServiceFactory(stf, mimeTypeFactory, g_buildServiceGroupFactory); 00427 (void) new KBuildProtocolInfoFactory(); 00428 00429 if( build()) // Parse dirs 00430 { 00431 save(str); // Save database 00432 if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full? 00433 database.abort(); // Error 00434 delete str; 00435 str = 0; 00436 if (!database.finalize()) 00437 { 00438 fprintf(stderr, "kbuildsycoca4: ERROR writing database '%s'!\n", database.fileName().toLocal8Bit().data()); 00439 fprintf(stderr, "kbuildsycoca4: Disk full?\n"); 00440 return false; 00441 } 00442 } 00443 else 00444 { 00445 delete str; 00446 str = 0; 00447 database.abort(); 00448 if (bMenuTest) 00449 return true; 00450 kDebug(7021) << "Database is up to date"; 00451 } 00452 00453 if (!bGlobalDatabase) 00454 { 00455 // update the timestamp file 00456 QString stamppath = path + "stamp"; 00457 QFile ksycocastamp(stamppath); 00458 ksycocastamp.open( QIODevice::WriteOnly ); 00459 QDataStream str( &ksycocastamp ); 00460 str.setVersion(QDataStream::Qt_3_1); 00461 str << newTimestamp; 00462 str << existingResourceDirs(); 00463 if (g_vfolder) 00464 str << g_vfolder->allDirectories(); // Extra resource dirs 00465 } 00466 if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile) 00467 KMemFile::fileContentsChanged(path); 00468 00469 return true; 00470 } 00471 00472 void KBuildSycoca::save(QDataStream* str) 00473 { 00474 // Write header (#pass 1) 00475 str->device()->seek(0); 00476 00477 (*str) << (qint32) KSycoca::version(); 00478 KSycocaFactory * servicetypeFactory = 0; 00479 KBuildMimeTypeFactory * mimeTypeFactory = 0; 00480 KBuildServiceFactory * serviceFactory = 0; 00481 for(KSycocaFactoryList::Iterator factory = factories()->begin(); 00482 factory != factories()->end(); 00483 ++factory) 00484 { 00485 qint32 aId; 00486 qint32 aOffset; 00487 aId = (*factory)->factoryId(); 00488 if ( aId == KST_KServiceTypeFactory ) 00489 servicetypeFactory = *factory; 00490 else if ( aId == KST_KMimeTypeFactory ) 00491 mimeTypeFactory = static_cast<KBuildMimeTypeFactory *>( *factory ); 00492 else if ( aId == KST_KServiceFactory ) 00493 serviceFactory = static_cast<KBuildServiceFactory *>( *factory ); 00494 aOffset = (*factory)->offset(); // not set yet, so always 0 00495 (*str) << aId; 00496 (*str) << aOffset; 00497 } 00498 (*str) << (qint32) 0; // No more factories. 00499 // Write KDEDIRS 00500 (*str) << KGlobal::dirs()->kfsstnd_prefixes(); 00501 (*str) << newTimestamp; 00502 (*str) << KGlobal::locale()->language(); 00503 (*str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", 00504 KStandardDirs::Recursive ); 00505 (*str) << (*g_allResourceDirs); 00506 00507 // Calculate per-servicetype/mimetype data 00508 serviceFactory->postProcessServices(); 00509 00510 // Here so that it's the last debug message 00511 kDebug(7021) << "Saving"; 00512 00513 // Write factory data.... 00514 for(KSycocaFactoryList::Iterator factory = factories()->begin(); 00515 factory != factories()->end(); 00516 ++factory) 00517 { 00518 (*factory)->save(*str); 00519 if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full? 00520 return; // error 00521 } 00522 00523 int endOfData = str->device()->pos(); 00524 00525 // Write header (#pass 2) 00526 str->device()->seek(0); 00527 00528 (*str) << (qint32) KSycoca::version(); 00529 for(KSycocaFactoryList::Iterator factory = factories()->begin(); 00530 factory != factories()->end(); ++factory) 00531 { 00532 qint32 aId; 00533 qint32 aOffset; 00534 aId = (*factory)->factoryId(); 00535 aOffset = (*factory)->offset(); 00536 (*str) << aId; 00537 (*str) << aOffset; 00538 } 00539 (*str) << (qint32) 0; // No more factories. 00540 00541 // Jump to end of database 00542 str->device()->seek(endOfData); 00543 } 00544 00545 bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top ) 00546 { 00547 if( top ) 00548 { 00549 QFileInfo inf( dirname ); 00550 if( inf.lastModified() > stamp ) { 00551 kDebug( 7021 ) << "timestamp changed:" << dirname; 00552 return false; 00553 } 00554 } 00555 QDir dir( dirname ); 00556 const QFileInfoList list = dir.entryInfoList( QDir::NoFilter, QDir::Unsorted ); 00557 if (list.isEmpty()) 00558 return true; 00559 00560 foreach ( const QFileInfo& fi, list ) { 00561 if( fi.fileName() == "." || fi.fileName() == ".." ) 00562 continue; 00563 if( fi.lastModified() > stamp ) 00564 { 00565 kDebug( 7201 ) << "timestamp changed:" << fi.filePath(); 00566 return false; 00567 } 00568 if( fi.isDir() && !checkDirTimestamps( fi.filePath(), stamp, false )) 00569 return false; 00570 } 00571 return true; 00572 } 00573 00574 // check times of last modification of all files on which ksycoca depens, 00575 // and also their directories 00576 // if all of them are older than the timestamp in file ksycocastamp, this 00577 // means that there's no need to rebuild ksycoca 00578 bool KBuildSycoca::checkTimestamps( quint32 timestamp, const QStringList &dirs ) 00579 { 00580 kDebug( 7021 ) << "checking file timestamps"; 00581 QDateTime stamp; 00582 stamp.setTime_t( timestamp ); 00583 for( QStringList::ConstIterator it = dirs.begin(); 00584 it != dirs.end(); 00585 ++it ) 00586 { 00587 if( !checkDirTimestamps( *it, stamp, true )) 00588 return false; 00589 } 00590 kDebug( 7021 ) << "timestamps check ok"; 00591 return true; 00592 } 00593 00594 QStringList KBuildSycoca::existingResourceDirs() 00595 { 00596 static QStringList* dirs = NULL; 00597 if( dirs != NULL ) 00598 return *dirs; 00599 dirs = new QStringList; 00600 g_allResourceDirs = new QStringList; 00601 // these are all resources cached by ksycoca 00602 QStringList resources; 00603 resources += KBuildServiceTypeFactory::resourceTypes(); 00604 resources += KBuildMimeTypeFactory::resourceTypes(); 00605 resources += KBuildServiceGroupFactory::resourceTypes(); 00606 resources += KBuildServiceFactory::resourceTypes(); 00607 resources += KBuildProtocolInfoFactory::resourceTypes(); 00608 while( !resources.empty()) 00609 { 00610 QString res = resources.front(); 00611 *dirs += KGlobal::dirs()->resourceDirs( res.toLatin1()); 00612 resources.removeAll( res ); 00613 } 00614 00615 *g_allResourceDirs = *dirs; 00616 00617 for( QStringList::Iterator it = dirs->begin(); 00618 it != dirs->end(); ) 00619 { 00620 QFileInfo inf( *it ); 00621 if( !inf.exists() || !inf.isReadable() ) 00622 it = dirs->erase( it ); 00623 else 00624 ++it; 00625 } 00626 return *dirs; 00627 } 00628 00629 static const char appFullName[] = "org.kde.kbuildsycoca"; 00630 static const char appName[] = "kbuildsycoca4"; 00631 static const char appVersion[] = "1.1"; 00632 00633 extern "C" KDE_EXPORT int kdemain(int argc, char **argv) 00634 { 00635 KAboutData d(appName, "kdelibs4", ki18n("KBuildSycoca"), appVersion, 00636 ki18n("Rebuilds the system configuration cache."), 00637 KAboutData::License_GPL, ki18n("(c) 1999-2002 KDE Developers")); 00638 d.addAuthor(ki18n("David Faure"), ki18n("Author"), "faure@kde.org"); 00639 d.addAuthor(ki18n("Waldo Bastian"), ki18n("Author"), "bastian@kde.org"); 00640 00641 KCmdLineOptions options; 00642 options.add("nosignal", ki18n("Do not signal applications to update")); 00643 options.add("noincremental", ki18n("Disable incremental update, re-read everything")); 00644 options.add("checkstamps", ki18n("Check file timestamps")); 00645 options.add("nocheckfiles", ki18n("Disable checking files (dangerous)")); 00646 options.add("global", ki18n("Create global database")); 00647 options.add("menutest", ki18n("Perform menu generation test run only")); 00648 options.add("track <menu-id>", ki18n("Track menu id for debug purposes")); 00649 00650 KCmdLineArgs::init(argc, argv, &d); 00651 KCmdLineArgs::addCmdLineOptions(options); 00652 KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); 00653 bGlobalDatabase = args->isSet("global"); 00654 bMenuTest = args->isSet("menutest"); 00655 00656 if (bGlobalDatabase) 00657 { 00658 setenv("KDEHOME", "-", 1); 00659 setenv("KDEROOTHOME", "-", 1); 00660 } 00661 00662 QCoreApplication k(argc, argv); 00663 KComponentData mainComponent(d); 00664 00665 #ifndef KBUILDSYCOCA_NO_KCRASH 00666 KCrash::setCrashHandler(KCrash::defaultCrashHandler); 00667 KCrash::setEmergencySaveFunction(crashHandler); 00668 KCrash::setApplicationName(QString(appName)); 00669 #endif 00670 00671 // force generating of KLocale object. if not, the database will get 00672 // be translated 00673 KGlobal::locale(); 00674 KGlobal::dirs()->addResourceType("app-reg", 0, "share/application-registry" ); 00675 00676 while(QDBusConnection::sessionBus().isConnected()) 00677 { 00678 // kapp registered already, but with the PID in the name. 00679 // We need to re-register without it, to detect already-running kbuildsycoca instances. 00680 if (QDBusConnection::sessionBus().interface()->registerService(appFullName, QDBusConnectionInterface::QueueService) 00681 != QDBusConnectionInterface::ServiceQueued) 00682 { 00683 break; // Go 00684 } 00685 fprintf(stderr, "Waiting for already running %s to finish.\n", appName); 00686 00687 QEventLoop eventLoop; 00688 QObject::connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceRegistered(QString)), 00689 &eventLoop, SLOT(quit())); 00690 eventLoop.exec( QEventLoop::ExcludeUserInputEvents ); 00691 } 00692 fprintf(stderr, "%s running...\n", appName); 00693 00694 bool checkfiles = bGlobalDatabase || args->isSet("checkfiles"); 00695 00696 bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles; 00697 if (incremental || !checkfiles) 00698 { 00699 KSycoca::disableAutoRebuild(); // Prevent deadlock 00700 QString current_language = KGlobal::locale()->language(); 00701 QString ksycoca_language = KSycoca::self()->language(); 00702 quint32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", 00703 KStandardDirs::Recursive ); 00704 quint32 ksycoca_update_sig = KSycoca::self()->updateSignature(); 00705 QString current_prefixes = KGlobal::dirs()->kfsstnd_prefixes(); 00706 QString ksycoca_prefixes = KSycoca::self()->kfsstnd_prefixes(); 00707 00708 if ((current_update_sig != ksycoca_update_sig) || 00709 (current_language != ksycoca_language) || 00710 (current_prefixes != ksycoca_prefixes) || 00711 (KSycoca::self()->timeStamp() == 0)) 00712 { 00713 incremental = false; 00714 checkfiles = true; 00715 KBuildSycoca::clearCaches(); 00716 } 00717 } 00718 00719 bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles; 00720 quint32 filestamp = 0; 00721 QStringList oldresourcedirs; 00722 if( checkstamps && incremental ) 00723 { 00724 QString path = sycocaPath()+"stamp"; 00725 QByteArray qPath = QFile::encodeName(path); 00726 cSycocaPath = qPath.data(); // Delete timestamps on crash 00727 QFile ksycocastamp(path); 00728 if( ksycocastamp.open( QIODevice::ReadOnly )) 00729 { 00730 QDataStream str( &ksycocastamp ); 00731 str.setVersion(QDataStream::Qt_3_1); 00732 00733 if (!str.atEnd()) 00734 str >> filestamp; 00735 if (!str.atEnd()) 00736 { 00737 str >> oldresourcedirs; 00738 if( oldresourcedirs != KBuildSycoca::existingResourceDirs()) 00739 checkstamps = false; 00740 } 00741 else 00742 { 00743 checkstamps = false; 00744 } 00745 if (!str.atEnd()) 00746 { 00747 QStringList extraResourceDirs; 00748 str >> extraResourceDirs; 00749 oldresourcedirs += extraResourceDirs; 00750 } 00751 } 00752 else 00753 { 00754 checkstamps = false; 00755 } 00756 cSycocaPath = 0; 00757 } 00758 00759 newTimestamp = (quint32) time(0); 00760 QStringList changedResources; 00761 00762 if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs ))) 00763 { 00764 QByteArray qSycocaPath = QFile::encodeName(sycocaPath()); 00765 cSycocaPath = qSycocaPath.data(); 00766 00767 g_allEntries = 0; 00768 g_ctimeDict = 0; 00769 if (incremental) 00770 { 00771 kDebug(7021) << "Reusing existing ksycoca"; 00772 KSycoca::self(); 00773 KSycocaFactoryList *factories = new KSycocaFactoryList; 00774 g_allEntries = new KSycocaEntryListList; 00775 g_ctimeDict = new KCTimeDict; 00776 00777 // Must be in same order as in KBuildSycoca::recreate()! 00778 factories->append( new KServiceTypeFactory ); 00779 factories->append( new KMimeTypeFactory ); 00780 factories->append( new KServiceGroupFactory ); 00781 factories->append( new KServiceFactory ); 00782 factories->append( new KProtocolInfoFactory ); 00783 00784 // For each factory 00785 for (KSycocaFactoryList::Iterator factory = factories->begin(); 00786 factory != factories->end(); ++factory) 00787 { 00788 const KSycocaEntry::List list = (*factory)->allEntries(); 00789 g_allEntries->append( list ); 00790 } 00791 delete factories; factories = 0; 00792 KCTimeInfo *ctimeInfo = new KCTimeInfo; 00793 *g_ctimeDict = ctimeInfo->loadDict(); 00794 } 00795 cSycocaPath = 0; 00796 00797 KBuildSycoca *sycoca = new KBuildSycoca; // Build data base (deletes oldSycoca) 00798 if (args->isSet("track")) 00799 sycoca->setTrackId(args->getOption("track")); 00800 if (!sycoca->recreate()) { 00801 return -1; 00802 } 00803 changedResources = sycoca->changedResources(); 00804 00805 if (bGlobalDatabase) 00806 { 00807 // These directories may have been created with 0700 permission 00808 // better delete them if they are empty 00809 QString applnkDir = KGlobal::dirs()->saveLocation("apps", QString(), false); 00810 ::rmdir(QFile::encodeName(applnkDir)); 00811 QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString(), false); 00812 ::rmdir(QFile::encodeName(servicetypesDir)); 00813 } 00814 } 00815 00816 if (args->isSet("signal")) 00817 { 00818 // Notify ALL applications that have a ksycoca object, using a signal 00819 QDBusMessage signal = QDBusMessage::createSignal("/", "org.kde.KSycoca", "notifyDatabaseChanged" ); 00820 signal << changedResources; 00821 00822 if (QDBusConnection::sessionBus().isConnected()) { 00823 kDebug() << "Emitting notifyDatabaseChanged" << changedResources; 00824 QDBusConnection::sessionBus().send(signal); 00825 qApp->processEvents(); // make sure the dbus signal is sent before we quit. 00826 } 00827 } 00828 00829 return 0; 00830 } 00831 00832 #include "kbuildsycoca.moc"
KDE 4.6 API Reference