kded.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 David Faure <faure@kde.org>
00003  *  Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation;
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include <qdir.h>
00021 
00022 #include "kded.h"
00023 #include "kdedmodule.h"
00024 
00025 #include <kresourcelist.h>
00026 #include <kcrash.h>
00027 
00028 #include <unistd.h>
00029 #include <stdlib.h>
00030 #include <signal.h>
00031 #include <time.h>
00032 
00033 #include <qfile.h>
00034 #include <qtimer.h>
00035 
00036 #include <dcopclient.h>
00037 
00038 #include <kuniqueapplication.h>
00039 #include <kcmdlineargs.h>
00040 #include <kaboutdata.h>
00041 #include <klocale.h>
00042 #include <kglobal.h>
00043 #include <kprocess.h>
00044 #include <kdebug.h>
00045 #include <kdirwatch.h>
00046 #include <kstandarddirs.h>
00047 #include <kdatastream.h>
00048 #include <kio/global.h>
00049 #include <kservicetype.h>
00050 
00051 #ifdef Q_WS_X11
00052 #include <X11/Xlib.h>
00053 #include <fixx11h.h>
00054 #endif
00055 
00056 Kded *Kded::_self = 0;
00057 
00058 static bool checkStamps = true;
00059 static bool delayedCheck = false;
00060 
00061 static void runBuildSycoca(QObject *callBackObj=0, const char *callBackSlot=0)
00062 {
00063    QStringList args;
00064    args.append("--incremental");
00065    if(checkStamps)
00066       args.append("--checkstamps");
00067    if(delayedCheck)
00068       args.append("--nocheckfiles");
00069    else
00070       checkStamps = false; // useful only during kded startup
00071    if (callBackObj)
00072    {
00073       QByteArray data;
00074       QDataStream dataStream( data, IO_WriteOnly );
00075       dataStream << QString("kbuildsycoca") << args;
00076       QCString _launcher = KApplication::launcher();
00077 
00078       kapp->dcopClient()->callAsync(_launcher, _launcher, "kdeinit_exec_wait(QString,QStringList)", data, callBackObj, callBackSlot);
00079    }
00080    else
00081    {
00082       KApplication::kdeinitExecWait( "kbuildsycoca", args );
00083    }
00084 }
00085 
00086 static void runKonfUpdate()
00087 {
00088    KApplication::kdeinitExecWait( "kconf_update", QStringList(), 0, 0, "0" /*no startup notification*/ );
00089 }
00090 
00091 static void runDontChangeHostname(const QCString &oldName, const QCString &newName)
00092 {
00093    QStringList args;
00094    args.append(QFile::decodeName(oldName));
00095    args.append(QFile::decodeName(newName));
00096    KApplication::kdeinitExecWait( "kdontchangethehostname", args );
00097 }
00098 
00099 Kded::Kded(bool checkUpdates, bool new_startup)
00100   : DCOPObject("kbuildsycoca"), DCOPObjectProxy(),
00101     b_checkUpdates(checkUpdates),
00102     m_needDelayedCheck(false),
00103     m_newStartup( new_startup )
00104 {
00105   _self = this;
00106   QCString cPath;
00107   QCString ksycoca_env = getenv("KDESYCOCA");
00108   if (ksycoca_env.isEmpty())
00109      cPath = QFile::encodeName(KGlobal::dirs()->saveLocation("tmp")+"ksycoca");
00110   else
00111      cPath = ksycoca_env;
00112   m_pTimer = new QTimer(this);
00113   connect(m_pTimer, SIGNAL(timeout()), this, SLOT(recreate()));
00114 
00115   QTimer::singleShot(100, this, SLOT(installCrashHandler()));
00116 
00117   m_pDirWatch = 0;
00118 
00119   m_windowIdList.setAutoDelete(true);
00120 
00121   m_recreateCount = 0;
00122   m_recreateBusy = false;
00123 }
00124 
00125 Kded::~Kded()
00126 {
00127   _self = 0;
00128   m_pTimer->stop();
00129   delete m_pTimer;
00130   delete m_pDirWatch;
00131 
00132   // We have to delete the modules while we're still able to process incoming
00133   // DCOP messages, since modules might make DCOP calls in their destructors.
00134   QAsciiDictIterator<KDEDModule> it(m_modules);
00135   for(; it.current(); ++it)
00136     delete it.current();
00137 }
00138 
00139 bool Kded::process(const QCString &obj, const QCString &fun,
00140                    const QByteArray &data,
00141                    QCString &replyType, QByteArray &replyData)
00142 {
00143   if (obj == "ksycoca") return false; // Ignore this one.
00144 
00145   if (m_dontLoad[obj])
00146      return false;
00147 
00148   KDEDModule *module = loadModule(obj, true);
00149   if (!module)
00150      return false;
00151 
00152   module->setCallingDcopClient(kapp->dcopClient());
00153   return module->process(fun, data, replyType, replyData);
00154 }
00155 
00156 void Kded::initModules()
00157 {
00158      m_dontLoad.clear();
00159      KConfig *config = kapp->config();
00160      bool kde_running = !( getenv( "KDE_FULL_SESSION" ) == NULL || getenv( "KDE_FULL_SESSION" )[ 0 ] == '\0' );
00161     // not the same user like the one running the session (most likely we're run via sudo or something)
00162     if( getenv( "KDE_SESSION_UID" ) != NULL && uid_t( atoi( getenv( "KDE_SESSION_UID" ))) != getuid())
00163         kde_running = false;
00164      // Preload kded modules.
00165      KService::List kdedModules = KServiceType::offers("KDEDModule");
00166      for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
00167      {
00168          KService::Ptr service = *it;
00169          bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
00170          config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
00171          autoload = config->readBoolEntry("autoload", autoload);
00172          if( m_newStartup )
00173          {
00174             // see ksmserver's README for description of the phases
00175             QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
00176             int phase = phasev.isValid() ? phasev.toInt() : 2;
00177             bool prevent_autoload = false;
00178             switch( phase )
00179             {
00180                 case 0: // always autoload
00181                     break;
00182                 case 1: // autoload only in KDE
00183                     if( !kde_running )
00184                         prevent_autoload = true;
00185                     break;
00186                 case 2: // autoload delayed, only in KDE
00187                 default:
00188                     prevent_autoload = true;
00189                     break;   
00190             }
00191             if (autoload && !prevent_autoload)
00192                loadModule(service, false);
00193          }
00194          else
00195          {
00196             if (autoload && kde_running)
00197                loadModule(service, false);
00198          }
00199          bool dontLoad = false;
00200          QVariant p = service->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
00201          if (p.isValid() && (p.toBool() == false))
00202             dontLoad = true;
00203          if (dontLoad)
00204             noDemandLoad(service->desktopEntryName());
00205 
00206          if (dontLoad && !autoload)
00207             unloadModule(service->desktopEntryName().latin1());
00208      }
00209 }
00210 
00211 void Kded::loadSecondPhase()
00212 {
00213      kdDebug(7020) << "Loading second phase autoload" << endl;
00214      KConfig *config = kapp->config();
00215      KService::List kdedModules = KServiceType::offers("KDEDModule");
00216      for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
00217      {
00218          KService::Ptr service = *it;
00219          bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
00220          config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
00221          autoload = config->readBoolEntry("autoload", autoload);
00222          QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
00223          int phase = phasev.isValid() ? phasev.toInt() : 2;
00224          if( phase == 2 && autoload )
00225             loadModule(service, false);
00226      }
00227 }
00228 
00229 void Kded::noDemandLoad(const QString &obj)
00230 {
00231   m_dontLoad.insert(obj.latin1(), this);
00232 }
00233 
00234 KDEDModule *Kded::loadModule(const QCString &obj, bool onDemand)
00235 {
00236   KDEDModule *module = m_modules.find(obj);
00237   if (module)
00238      return module;
00239   KService::Ptr s = KService::serviceByDesktopPath("kded/"+obj+".desktop");
00240   return loadModule(s, onDemand);
00241 }
00242 
00243 KDEDModule *Kded::loadModule(const KService *s, bool onDemand)
00244 {
00245   KDEDModule *module = 0;
00246   if (s && !s->library().isEmpty())
00247   {
00248     QCString obj = s->desktopEntryName().latin1();
00249     KDEDModule *oldModule = m_modules.find(obj);
00250     if (oldModule)
00251        return oldModule;
00252 
00253     if (onDemand)
00254     {
00255       QVariant p = s->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
00256       if (p.isValid() && (p.toBool() == false))
00257       {
00258          noDemandLoad(s->desktopEntryName());
00259          return 0;
00260       }
00261     }
00262     // get the library loader instance
00263 
00264     KLibLoader *loader = KLibLoader::self();
00265 
00266     QVariant v = s->property("X-KDE-FactoryName", QVariant::String);
00267     QString factory = v.isValid() ? v.toString() : QString::null;
00268     if (factory.isEmpty())
00269     {
00270        // Stay bugward compatible
00271        v = s->property("X-KDE-Factory", QVariant::String);
00272        factory = v.isValid() ? v.toString() : QString::null;
00273     }
00274     if (factory.isEmpty())
00275       factory = s->library();
00276 
00277     factory = "create_" + factory;
00278     QString libname = "kded_"+s->library();
00279 
00280     KLibrary *lib = loader->library(QFile::encodeName(libname));
00281     if (!lib)
00282     {
00283       kdWarning() << k_funcinfo << "Could not load library. [ "
00284           << loader->lastErrorMessage() << " ]" << endl;
00285       libname.prepend("lib");
00286       lib = loader->library(QFile::encodeName(libname));
00287     }
00288     if (lib)
00289     {
00290       // get the create_ function
00291       void *create = lib->symbol(QFile::encodeName(factory));
00292 
00293       if (create)
00294       {
00295         // create the module
00296         KDEDModule* (*func)(const QCString &);
00297         func = (KDEDModule* (*)(const QCString &)) create;
00298         module = func(obj);
00299         if (module)
00300         {
00301           m_modules.insert(obj, module);
00302           m_libs.insert(obj, lib);
00303           connect(module, SIGNAL(moduleDeleted(KDEDModule *)), SLOT(slotKDEDModuleRemoved(KDEDModule *)));
00304           kdDebug(7020) << "Successfully loaded module '" << obj << "'\n";
00305           return module;
00306         }
00307       }
00308       loader->unloadLibrary(QFile::encodeName(libname));
00309     }
00310     else
00311     {
00312     kdWarning() << k_funcinfo << "Could not load library. [ "
00313             << loader->lastErrorMessage() << " ]" << endl;
00314     }
00315     kdDebug(7020) << "Could not load module '" << obj << "'\n";
00316   }
00317   return 0;
00318 }
00319 
00320 bool Kded::unloadModule(const QCString &obj)
00321 {
00322   KDEDModule *module = m_modules.take(obj);
00323   if (!module)
00324      return false;
00325   kdDebug(7020) << "Unloading module '" << obj << "'\n";
00326   delete module;
00327   return true;
00328 }
00329 
00330 // DCOP
00331 QCStringList Kded::loadedModules()
00332 {
00333     QCStringList modules;
00334     QAsciiDictIterator<KDEDModule> it( m_modules );
00335     for ( ; it.current(); ++it)
00336         modules.append( it.currentKey() );
00337 
00338     return modules;
00339 }
00340 
00341 QCStringList Kded::functions()
00342 {
00343     QCStringList res = DCOPObject::functions();
00344     res += "ASYNC recreate()";
00345     return res;
00346 }
00347 
00348 void Kded::slotKDEDModuleRemoved(KDEDModule *module)
00349 {
00350   m_modules.remove(module->objId());
00351   KLibrary *lib = m_libs.take(module->objId());
00352   if (lib)
00353      lib->unload();
00354 }
00355 
00356 void Kded::slotApplicationRemoved(const QCString &appId)
00357 {
00358   for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
00359   {
00360      it.current()->removeAll(appId);
00361   }
00362 
00363   QValueList<long> *windowIds = m_windowIdList.find(appId);
00364   if (windowIds)
00365   {
00366      for( QValueList<long>::ConstIterator it = windowIds->begin();
00367           it != windowIds->end(); ++it)
00368      {
00369         long windowId = *it;
00370         m_globalWindowIdList.remove(windowId);
00371         for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
00372         {
00373             emit it.current()->windowUnregistered(windowId);
00374         }
00375      }
00376      m_windowIdList.remove(appId);
00377   }
00378 }
00379 
00380 void Kded::updateDirWatch()
00381 {
00382   if (!b_checkUpdates) return;
00383 
00384   delete m_pDirWatch;
00385   m_pDirWatch = new KDirWatch;
00386 
00387   QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
00388            this, SLOT(update(const QString&)));
00389   QObject::connect( m_pDirWatch, SIGNAL(created(const QString&)),
00390            this, SLOT(update(const QString&)));
00391   QObject::connect( m_pDirWatch, SIGNAL(deleted(const QString&)),
00392            this, SLOT(dirDeleted(const QString&)));
00393 
00394   // For each resource
00395   for( QStringList::ConstIterator it = m_allResourceDirs.begin();
00396        it != m_allResourceDirs.end();
00397        ++it )
00398   {
00399      readDirectory( *it );
00400   }
00401 }
00402 
00403 void Kded::updateResourceList()
00404 {
00405   delete KSycoca::self();
00406 
00407   if (!b_checkUpdates) return;
00408 
00409   if (delayedCheck) return;
00410 
00411   QStringList dirs = KSycoca::self()->allResourceDirs();
00412   // For each resource
00413   for( QStringList::ConstIterator it = dirs.begin();
00414        it != dirs.end();
00415        ++it )
00416   {
00417      if (m_allResourceDirs.find(*it) == m_allResourceDirs.end())
00418      {
00419         m_allResourceDirs.append(*it);
00420         readDirectory(*it);
00421      }
00422   }
00423 }
00424 
00425 void Kded::crashHandler(int)
00426 {
00427    DCOPClient::emergencyClose();
00428    if (_self) // Don't restart if we were closing down
00429       system("kded");
00430 qWarning("Last DCOP call before KDED crash was from application '%s'\n"
00431          "to object '%s', function '%s'.",
00432          DCOPClient::postMortemSender(),
00433          DCOPClient::postMortemObject(),
00434          DCOPClient::postMortemFunction());
00435 }
00436 
00437 void Kded::installCrashHandler()
00438 {
00439    KCrash::setEmergencySaveFunction(crashHandler);
00440 }
00441 
00442 void Kded::recreate()
00443 {
00444    recreate(false);
00445 }
00446 
00447 void Kded::runDelayedCheck()
00448 {
00449    if( m_needDelayedCheck )
00450       recreate(false);
00451    m_needDelayedCheck = false;
00452 }
00453 
00454 void Kded::recreate(bool initial)
00455 {
00456    m_recreateBusy = true;
00457    // Using KLauncher here is difficult since we might not have a
00458    // database
00459 
00460    if (!initial)
00461    {
00462       updateDirWatch(); // Update tree first, to be sure to miss nothing.
00463       runBuildSycoca(this, SLOT(recreateDone()));
00464    }
00465    else
00466    {
00467       if(!delayedCheck)
00468          updateDirWatch(); // this would search all the directories
00469       runBuildSycoca();
00470       recreateDone();
00471       if(delayedCheck)
00472       {
00473          // do a proper ksycoca check after a delay
00474          QTimer::singleShot( 60000, this, SLOT( runDelayedCheck()));
00475          m_needDelayedCheck = true;
00476          delayedCheck = false;
00477       }
00478       else
00479          m_needDelayedCheck = false;
00480    }
00481 }
00482 
00483 void Kded::recreateDone()
00484 {
00485    updateResourceList();
00486 
00487    for(; m_recreateCount; m_recreateCount--)
00488    {
00489       QCString replyType = "void";
00490       QByteArray replyData;
00491       DCOPClientTransaction *transaction = m_recreateRequests.first();
00492       if (transaction)
00493          kapp->dcopClient()->endTransaction(transaction, replyType, replyData);
00494       m_recreateRequests.remove(m_recreateRequests.begin());
00495    }
00496    m_recreateBusy = false;
00497 
00498    // Did a new request come in while building?
00499    if (!m_recreateRequests.isEmpty())
00500    {
00501       m_pTimer->start(2000, true /* single shot */ );
00502       m_recreateCount = m_recreateRequests.count();
00503    }
00504 }
00505 
00506 void Kded::dirDeleted(const QString& path)
00507 {
00508   update(path);
00509 }
00510 
00511 void Kded::update(const QString& )
00512 {
00513   if (!m_recreateBusy)
00514   {
00515     m_pTimer->start( 2000, true /* single shot */ );
00516   }
00517   else
00518   {
00519     m_recreateRequests.append(0);
00520   }
00521 }
00522 
00523 bool Kded::process(const QCString &fun, const QByteArray &data,
00524                            QCString &replyType, QByteArray &replyData)
00525 {
00526   if (fun == "recreate()") {
00527     if (!m_recreateBusy)
00528     {
00529        if (m_recreateRequests.isEmpty())
00530        {
00531           m_pTimer->start(0, true /* single shot */ );
00532           m_recreateCount = 0;
00533        }
00534        m_recreateCount++;
00535     }
00536     m_recreateRequests.append(kapp->dcopClient()->beginTransaction());
00537     replyType = "void";
00538     return true;
00539   } else {
00540     return DCOPObject::process(fun, data, replyType, replyData);
00541   }
00542 }
00543 
00544 
00545 void Kded::readDirectory( const QString& _path )
00546 {
00547   QString path( _path );
00548   if ( path.right(1) != "/" )
00549     path += "/";
00550 
00551   if ( m_pDirWatch->contains( path ) ) // Already seen this one?
00552      return;
00553 
00554   QDir d( _path, QString::null, QDir::Unsorted, QDir::Readable | QDir::Executable | QDir::Dirs | QDir::Hidden );
00555   // set QDir ...
00556 
00557 
00558   //************************************************************************
00559   //                           Setting dirs
00560   //************************************************************************
00561 
00562   m_pDirWatch->addDir(path);          // add watch on this dir
00563 
00564   if ( !d.exists() )                            // exists&isdir?
00565   {
00566     kdDebug(7020) << QString("Does not exist! (%1)").arg(_path) << endl;
00567     return;                             // return false
00568   }
00569 
00570   // Note: If some directory is gone, dirwatch will delete it from the list.
00571 
00572   //************************************************************************
00573   //                               Reading
00574   //************************************************************************
00575   QString file;
00576   unsigned int i;                           // counter and string length.
00577   unsigned int count = d.count();
00578   for( i = 0; i < count; i++ )                        // check all entries
00579   {
00580      if (d[i] == "." || d[i] == ".." || d[i] == "magic")
00581        continue;                          // discard those ".", "..", "magic"...
00582 
00583      file = path;                           // set full path
00584      file += d[i];                          // and add the file name.
00585 
00586      readDirectory( file );      // yes, dive into it.
00587   }
00588 }
00589 
00590 bool Kded::isWindowRegistered(long windowId)
00591 {
00592   return m_globalWindowIdList.find(windowId) != 0;
00593 
00594 }
00595 
00596 // DCOP
00597 void Kded::registerWindowId(long windowId)
00598 {
00599   m_globalWindowIdList.replace(windowId, &windowId);
00600   QCString sender = callingDcopClient()->senderId();
00601   if( sender.isEmpty()) // local call
00602       sender = callingDcopClient()->appId();
00603   QValueList<long> *windowIds = m_windowIdList.find(sender);
00604   if (!windowIds)
00605   {
00606     windowIds = new QValueList<long>;
00607     m_windowIdList.insert(sender, windowIds);
00608   }
00609   windowIds->append(windowId);
00610 
00611 
00612   for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
00613   {
00614      emit it.current()->windowRegistered(windowId);
00615   }
00616 }
00617 
00618 // DCOP
00619 void Kded::unregisterWindowId(long windowId)
00620 {
00621   m_globalWindowIdList.remove(windowId);
00622   QCString sender = callingDcopClient()->senderId();
00623   if( sender.isEmpty()) // local call
00624       sender = callingDcopClient()->appId();
00625   QValueList<long> *windowIds = m_windowIdList.find(sender);
00626   if (windowIds)
00627   {
00628      windowIds->remove(windowId);
00629      if (windowIds->isEmpty())
00630         m_windowIdList.remove(sender);
00631   }
00632 
00633   for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
00634   {
00635     emit it.current()->windowUnregistered(windowId);
00636   }
00637 }
00638 
00639 
00640 static void sighandler(int /*sig*/)
00641 {
00642     if (kapp)
00643        kapp->quit();
00644 }
00645 
00646 KUpdateD::KUpdateD()
00647 {
00648     m_pDirWatch = new KDirWatch;
00649     m_pTimer = new QTimer;
00650     connect(m_pTimer, SIGNAL(timeout()), this, SLOT(runKonfUpdate()));
00651     QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
00652            this, SLOT(slotNewUpdateFile()));
00653 
00654     QStringList dirs = KGlobal::dirs()->findDirs("data", "kconf_update");
00655     for( QStringList::ConstIterator it = dirs.begin();
00656          it != dirs.end();
00657          ++it )
00658     {
00659        QString path = *it;
00660        if (path[path.length()-1] != '/')
00661           path += "/";
00662 
00663        if (!m_pDirWatch->contains(path))
00664           m_pDirWatch->addDir(path);
00665     }
00666 }
00667 
00668 KUpdateD::~KUpdateD()
00669 {
00670     delete m_pDirWatch;
00671     delete m_pTimer;
00672 }
00673 
00674 void KUpdateD::runKonfUpdate()
00675 {
00676     ::runKonfUpdate();
00677 }
00678 
00679 void KUpdateD::slotNewUpdateFile()
00680 {
00681     m_pTimer->start( 500, true /* single shot */ );
00682 }
00683 
00684 KHostnameD::KHostnameD(int pollInterval)
00685 {
00686     m_Timer.start(pollInterval, false /* repetitive */ );
00687     connect(&m_Timer, SIGNAL(timeout()), this, SLOT(checkHostname()));
00688     checkHostname();
00689 }
00690 
00691 KHostnameD::~KHostnameD()
00692 {
00693     // Empty
00694 }
00695 
00696 void KHostnameD::checkHostname()
00697 {
00698     char buf[1024+1];
00699     if (gethostname(buf, 1024) != 0)
00700        return;
00701     buf[sizeof(buf)-1] = '\0';
00702 
00703     if (m_hostname.isEmpty())
00704     {
00705        m_hostname = buf;
00706        return;
00707     }
00708 
00709     if (m_hostname == buf)
00710        return;
00711 
00712     QCString newHostname = buf;
00713 
00714     runDontChangeHostname(m_hostname, newHostname);
00715     m_hostname = newHostname;
00716 }
00717 
00718 
00719 static KCmdLineOptions options[] =
00720 {
00721   { "check", I18N_NOOP("Check Sycoca database only once"), 0 },
00722   { "new-startup", "Internal", 0 },
00723   KCmdLineLastOption
00724 };
00725 
00726 class KDEDQtDCOPObject : public DCOPObject
00727 {
00728 public:
00729   KDEDQtDCOPObject() : DCOPObject("qt/kded") { }
00730 
00731   virtual bool process(const QCString &fun, const QByteArray &data,
00732                        QCString& replyType, QByteArray &replyData)
00733     {
00734       if ( kapp && (fun == "quit()") )
00735       {
00736         kapp->quit();
00737         replyType = "void";
00738         return true;
00739       }
00740       return DCOPObject::process(fun, data, replyType, replyData);
00741     }
00742 
00743   QCStringList functions()
00744     {
00745        QCStringList res = DCOPObject::functions();
00746        res += "void quit()";
00747        return res;
00748     }
00749 };
00750 
00751 class KDEDApplication : public KUniqueApplication
00752 {
00753 public:
00754   KDEDApplication() : KUniqueApplication( )
00755     {
00756        startup = true;
00757        dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()",
00758                                         objId(), "quit()", false );
00759     }
00760 
00761   int newInstance()
00762     {
00763        if (startup) {
00764           startup = false;
00765           if( Kded::self()->newStartup())
00766              Kded::self()->initModules();
00767           else
00768          QTimer::singleShot(500, Kded::self(), SLOT(initModules()));
00769        } else 
00770           runBuildSycoca();
00771 
00772        return 0;
00773     }
00774 
00775   QCStringList functions()
00776     {
00777        QCStringList res = KUniqueApplication::functions();
00778        res += "bool loadModule(QCString)";
00779        res += "bool unloadModule(QCString)";
00780        res += "void registerWindowId(long int)";
00781        res += "void unregisterWindowId(long int)";
00782        res += "QCStringList loadedModules()";
00783        res += "void reconfigure()";
00784        res += "void loadSecondPhase()";
00785        res += "void quit()";
00786        return res;
00787     }
00788 
00789   bool process(const QCString &fun, const QByteArray &data,
00790                QCString &replyType, QByteArray &replyData)
00791   {
00792     if (fun == "loadModule(QCString)") {
00793       QCString module;
00794       QDataStream arg( data, IO_ReadOnly );
00795       arg >> module;
00796       bool result = (Kded::self()->loadModule(module, false) != 0);
00797       replyType = "bool";
00798       QDataStream _replyStream( replyData, IO_WriteOnly );
00799       _replyStream << result;
00800       return true;
00801     }
00802     else if (fun == "unloadModule(QCString)") {
00803       QCString module;
00804       QDataStream arg( data, IO_ReadOnly );
00805       arg >> module;
00806       bool result = Kded::self()->unloadModule(module);
00807       replyType = "bool";
00808       QDataStream _replyStream( replyData, IO_WriteOnly );
00809       _replyStream << result;
00810       return true;
00811     }
00812     else if (fun == "registerWindowId(long int)") {
00813       long windowId;
00814       QDataStream arg( data, IO_ReadOnly );
00815       arg >> windowId;
00816       Kded::self()->setCallingDcopClient(callingDcopClient());
00817       Kded::self()->registerWindowId(windowId);
00818       replyType = "void";
00819       return true;
00820     }
00821      else if (fun == "unregisterWindowId(long int)") {
00822       long windowId;
00823       QDataStream arg( data, IO_ReadOnly );
00824       arg >> windowId;
00825       Kded::self()->setCallingDcopClient(callingDcopClient());
00826       Kded::self()->unregisterWindowId(windowId);
00827       replyType = "void";
00828       return true;
00829     }
00830     else if (fun == "loadedModules()") {
00831       replyType = "QCStringList";
00832       QDataStream _replyStream(replyData, IO_WriteOnly);
00833       _replyStream << Kded::self()->loadedModules();
00834       return true;
00835     }
00836     else if (fun == "reconfigure()") {
00837       config()->reparseConfiguration();
00838       Kded::self()->initModules();
00839       replyType = "void";
00840       return true;
00841     }
00842     else if (fun == "loadSecondPhase()") {
00843       Kded::self()->loadSecondPhase();
00844       replyType = "void";
00845       return true;
00846     }
00847     else if (fun == "quit()") {
00848       quit();
00849       replyType = "void";
00850       return true;
00851     }
00852     return KUniqueApplication::process(fun, data, replyType, replyData);
00853   }
00854 
00855   bool startup;
00856   KDEDQtDCOPObject kdedQtDcopObject;
00857 };
00858 
00859 extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
00860 {
00861      KAboutData aboutData( "kded", I18N_NOOP("KDE Daemon"),
00862         "$Id: kded.cpp 634204 2007-02-16 16:12:23Z lunakl $",
00863         I18N_NOOP("KDE Daemon - triggers Sycoca database updates when needed"));
00864 
00865      KApplication::installSigpipeHandler();
00866 
00867      KCmdLineArgs::init(argc, argv, &aboutData);
00868 
00869      KUniqueApplication::addCmdLineOptions();
00870 
00871      KCmdLineArgs::addCmdLineOptions( options );
00872 
00873      // this program is in kdelibs so it uses kdelibs as catalog
00874      KLocale::setMainCatalogue("kdelibs");
00875 
00876      // WABA: Make sure not to enable session management.
00877      putenv(strdup("SESSION_MANAGER="));
00878 
00879      // Parse command line before checking DCOP
00880      KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00881 
00882      // Check DCOP communication.
00883      {
00884         DCOPClient testDCOP;
00885         QCString dcopName = testDCOP.registerAs("kded", false);
00886         if (dcopName.isEmpty())
00887         {
00888            kdFatal() << "DCOP communication problem!" << endl;
00889            return 1;
00890         }
00891      }
00892 
00893      KInstance *instance = new KInstance(&aboutData);
00894      KConfig *config = instance->config(); // Enable translations.
00895 
00896      if (args->isSet("check"))
00897      {
00898         config->setGroup("General");
00899         checkStamps = config->readBoolEntry("CheckFileStamps", true);
00900         runBuildSycoca();
00901         runKonfUpdate();
00902         exit(0);
00903      }
00904 
00905      if (!KUniqueApplication::start())
00906      {
00907         fprintf(stderr, "KDE Daemon (kded) already running.\n");
00908         exit(0);
00909      }
00910 
00911      KUniqueApplication::dcopClient()->setQtBridgeEnabled(false);
00912 
00913      config->setGroup("General");
00914      int HostnamePollInterval = config->readNumEntry("HostnamePollInterval", 5000);
00915      bool bCheckSycoca = config->readBoolEntry("CheckSycoca", true);
00916      bool bCheckUpdates = config->readBoolEntry("CheckUpdates", true);
00917      bool bCheckHostname = config->readBoolEntry("CheckHostname", true);
00918      checkStamps = config->readBoolEntry("CheckFileStamps", true);
00919      delayedCheck = config->readBoolEntry("DelayedCheck", false);
00920 
00921      Kded *kded = new Kded(bCheckSycoca, args->isSet("new-startup")); // Build data base
00922 
00923      signal(SIGTERM, sighandler);
00924      signal(SIGHUP, sighandler);
00925      KDEDApplication k;
00926 
00927      kded->recreate(true); // initial
00928 
00929      if (bCheckUpdates)
00930         (void) new KUpdateD; // Watch for updates
00931 
00932      runKonfUpdate(); // Run it once.
00933 
00934      if (bCheckHostname)
00935         (void) new KHostnameD(HostnamePollInterval); // Watch for hostname changes
00936 
00937      DCOPClient *client = kapp->dcopClient();
00938      QObject::connect(client, SIGNAL(applicationRemoved(const QCString&)),
00939              kded, SLOT(slotApplicationRemoved(const QCString&)));
00940      client->setNotifications(true);
00941      client->setDaemonMode( true );
00942 
00943      // During startup kdesktop waits for KDED to finish.
00944      // Send a notifyDatabaseChanged signal even if the database hasn't
00945      // changed.
00946      // If the database changed, kbuildsycoca's signal didn't go anywhere
00947      // anyway, because it was too early, so let's send this signal
00948      // unconditionnally (David)
00949      QByteArray data;
00950      client->send( "*", "ksycoca", "notifyDatabaseChanged()", data );
00951      client->send( "ksplash", "", "upAndRunning(QString)",  QString("kded"));
00952 #ifdef Q_WS_X11
00953      XEvent e;
00954      e.xclient.type = ClientMessage;
00955      e.xclient.message_type = XInternAtom( qt_xdisplay(), "_KDE_SPLASH_PROGRESS", False );
00956      e.xclient.display = qt_xdisplay();
00957      e.xclient.window = qt_xrootwin();
00958      e.xclient.format = 8;
00959      strcpy( e.xclient.data.b, "kded" );
00960      XSendEvent( qt_xdisplay(), qt_xrootwin(), False, SubstructureNotifyMask, &e );
00961 #endif
00962      int result = k.exec(); // keep running
00963 
00964      delete kded;
00965      delete instance; // Deletes config as well
00966 
00967      return result;
00968 }
00969 
00970 #include "kded.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys