1
/* This file is part of the KDE libraries
2
* Copyright (C) 1999 David Faure <faure@kde.org>
3
* Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Library General Public
7
* License version 2 as published by the Free Software Foundation;
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public License
15
* along with this library; see the file COPYING.LIB. If not, write to
16
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
23
#include "kdedmodule.h"
25
#include <kresourcelist.h>
36
#include <dcopclient.h>
38
#include <kuniqueapplication.h>
39
#include <kcmdlineargs.h>
40
#include <kaboutdata.h>
45
#include <kdirwatch.h>
46
#include <kstandarddirs.h>
47
#include <kdatastream.h>
48
#include <kio/global.h>
49
#include <kservicetype.h>
56
Kded *Kded::_self = 0;
58
static bool checkStamps = true;
59
static bool delayedCheck = false;
61
static void runBuildSycoca(QObject *callBackObj=0, const char *callBackSlot=0)
64
args.append("--incremental");
66
args.append("--checkstamps");
68
args.append("--nocheckfiles");
70
checkStamps = false; // useful only during kded startup
74
QDataStream dataStream( data, IO_WriteOnly );
75
dataStream << QString("kbuildsycoca") << args;
76
QCString _launcher = KApplication::launcher();
78
kapp->dcopClient()->callAsync(_launcher, _launcher, "kdeinit_exec_wait(QString,QStringList)", data, callBackObj, callBackSlot);
82
KApplication::kdeinitExecWait( "kbuildsycoca", args );
86
static void runKonfUpdate()
88
KApplication::kdeinitExecWait( "kconf_update", QStringList(), 0, 0, "0" /*no startup notification*/ );
91
static void runDontChangeHostname(const QCString &oldName, const QCString &newName)
94
args.append(QFile::decodeName(oldName));
95
args.append(QFile::decodeName(newName));
96
KApplication::kdeinitExecWait( "kdontchangethehostname", args );
99
Kded::Kded(bool checkUpdates, bool new_startup)
100
: DCOPObject("kbuildsycoca"), DCOPObjectProxy(),
101
b_checkUpdates(checkUpdates),
102
m_needDelayedCheck(false),
103
m_newStartup( new_startup )
107
QCString ksycoca_env = getenv("KDESYCOCA");
108
if (ksycoca_env.isEmpty())
109
cPath = QFile::encodeName(KGlobal::dirs()->saveLocation("tmp")+"ksycoca");
112
m_pTimer = new QTimer(this);
113
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(recreate()));
115
QTimer::singleShot(100, this, SLOT(installCrashHandler()));
119
m_windowIdList.setAutoDelete(true);
122
m_recreateBusy = false;
131
// We have to delete the modules while we're still able to process incoming
132
// DCOP messages, since modules might make DCOP calls in their destructors.
133
QAsciiDictIterator<KDEDModule> it(m_modules);
134
while (!it.isEmpty())
138
bool Kded::process(const QCString &obj, const QCString &fun,
139
const QByteArray &data,
140
QCString &replyType, QByteArray &replyData)
142
if (obj == "ksycoca") return false; // Ignore this one.
147
KDEDModule *module = loadModule(obj, true);
151
module->setCallingDcopClient(kapp->dcopClient());
152
return module->process(fun, data, replyType, replyData);
155
void Kded::initModules()
158
KConfig *config = kapp->config();
159
bool kde_running = !( getenv( "KDE_FULL_SESSION" ) == NULL || getenv( "KDE_FULL_SESSION" )[ 0 ] == '\0' );
160
// not the same user like the one running the session (most likely we're run via sudo or something)
161
if( getenv( "KDE_SESSION_UID" ) != NULL && uid_t( atoi( getenv( "KDE_SESSION_UID" ))) != getuid())
163
// Preload kded modules.
164
KService::List kdedModules = KServiceType::offers("KDEDModule");
165
for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
167
KService::Ptr service = *it;
168
bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
169
config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
170
autoload = config->readBoolEntry("autoload", autoload);
173
// see ksmserver's README for description of the phases
174
QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
175
int phase = phasev.isValid() ? phasev.toInt() : 2;
176
bool prevent_autoload = false;
179
case 0: // always autoload
181
case 1: // autoload only in KDE
183
prevent_autoload = true;
185
case 2: // autoload delayed, only in KDE
187
prevent_autoload = true;
190
if (autoload && !prevent_autoload)
191
loadModule(service, false);
195
if (autoload && kde_running)
196
loadModule(service, false);
198
bool dontLoad = false;
199
QVariant p = service->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
200
if (p.isValid() && (p.toBool() == false))
203
noDemandLoad(service->desktopEntryName());
205
if (dontLoad && !autoload)
206
unloadModule(service->desktopEntryName().latin1());
210
void Kded::loadSecondPhase()
212
kdDebug(7020) << "Loading second phase autoload" << endl;
213
KConfig *config = kapp->config();
214
KService::List kdedModules = KServiceType::offers("KDEDModule");
215
for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
217
KService::Ptr service = *it;
218
bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
219
config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
220
autoload = config->readBoolEntry("autoload", autoload);
221
QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
222
int phase = phasev.isValid() ? phasev.toInt() : 2;
223
if( phase == 2 && autoload )
224
loadModule(service, false);
228
void Kded::noDemandLoad(const QString &obj)
230
m_dontLoad.insert(obj.latin1(), this);
233
KDEDModule *Kded::loadModule(const QCString &obj, bool onDemand)
235
KDEDModule *module = m_modules.find(obj);
238
KService::Ptr s = KService::serviceByDesktopPath("kded/"+obj+".desktop");
239
return loadModule(s, onDemand);
242
KDEDModule *Kded::loadModule(const KService *s, bool onDemand)
244
KDEDModule *module = 0;
245
if (s && !s->library().isEmpty())
247
QCString obj = s->desktopEntryName().latin1();
248
KDEDModule *oldModule = m_modules.find(obj);
254
QVariant p = s->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
255
if (p.isValid() && (p.toBool() == false))
257
noDemandLoad(s->desktopEntryName());
261
// get the library loader instance
263
KLibLoader *loader = KLibLoader::self();
265
QVariant v = s->property("X-KDE-FactoryName", QVariant::String);
266
QString factory = v.isValid() ? v.toString() : QString::null;
267
if (factory.isEmpty())
269
// Stay bugward compatible
270
v = s->property("X-KDE-Factory", QVariant::String);
271
factory = v.isValid() ? v.toString() : QString::null;
273
if (factory.isEmpty())
274
factory = s->library();
276
factory = "create_" + factory;
277
QString libname = "kded_"+s->library();
279
KLibrary *lib = loader->library(QFile::encodeName(libname));
282
kdWarning() << k_funcinfo << "Could not load library. [ "
283
<< loader->lastErrorMessage() << " ]" << endl;
284
libname.prepend("lib");
285
lib = loader->library(QFile::encodeName(libname));
289
// get the create_ function
290
void *create = lib->symbol(QFile::encodeName(factory));
295
KDEDModule* (*func)(const QCString &);
296
func = (KDEDModule* (*)(const QCString &)) create;
300
m_modules.insert(obj, module);
301
m_libs.insert(obj, lib);
302
connect(module, SIGNAL(moduleDeleted(KDEDModule *)), SLOT(slotKDEDModuleRemoved(KDEDModule *)));
303
kdDebug(7020) << "Successfully loaded module '" << obj << "'\n";
307
loader->unloadLibrary(QFile::encodeName(libname));
311
kdWarning() << k_funcinfo << "Could not load library. [ "
312
<< loader->lastErrorMessage() << " ]" << endl;
314
kdDebug(7020) << "Could not load module '" << obj << "'\n";
319
bool Kded::unloadModule(const QCString &obj)
321
KDEDModule *module = m_modules.take(obj);
324
kdDebug(7020) << "Unloading module '" << obj << "'\n";
330
QCStringList Kded::loadedModules()
332
QCStringList modules;
333
QAsciiDictIterator<KDEDModule> it( m_modules );
334
for ( ; it.current(); ++it)
335
modules.append( it.currentKey() );
340
QCStringList Kded::functions()
342
QCStringList res = DCOPObject::functions();
343
res += "ASYNC recreate()";
347
void Kded::slotKDEDModuleRemoved(KDEDModule *module)
349
m_modules.remove(module->objId());
350
KLibrary *lib = m_libs.take(module->objId());
355
void Kded::slotApplicationRemoved(const QCString &appId)
357
for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
359
it.current()->removeAll(appId);
362
QValueList<long> *windowIds = m_windowIdList.find(appId);
365
for( QValueList<long>::ConstIterator it = windowIds->begin();
366
it != windowIds->end(); ++it)
369
m_globalWindowIdList.remove(windowId);
370
for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
372
emit it.current()->windowUnregistered(windowId);
375
m_windowIdList.remove(appId);
379
void Kded::updateDirWatch()
381
if (!b_checkUpdates) return;
384
m_pDirWatch = new KDirWatch;
386
QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
387
this, SLOT(update(const QString&)));
388
QObject::connect( m_pDirWatch, SIGNAL(created(const QString&)),
389
this, SLOT(update(const QString&)));
390
QObject::connect( m_pDirWatch, SIGNAL(deleted(const QString&)),
391
this, SLOT(dirDeleted(const QString&)));
394
for( QStringList::ConstIterator it = m_allResourceDirs.begin();
395
it != m_allResourceDirs.end();
398
readDirectory( *it );
402
void Kded::updateResourceList()
404
delete KSycoca::self();
406
if (!b_checkUpdates) return;
408
if (delayedCheck) return;
410
QStringList dirs = KSycoca::self()->allResourceDirs();
412
for( QStringList::ConstIterator it = dirs.begin();
416
if (m_allResourceDirs.find(*it) == m_allResourceDirs.end())
418
m_allResourceDirs.append(*it);
424
void Kded::crashHandler(int)
426
DCOPClient::emergencyClose();
427
if (_self) // Don't restart if we were closing down
429
qWarning("Last DCOP call before KDED crash was from application '%s'\n"
430
"to object '%s', function '%s'.",
431
DCOPClient::postMortemSender(),
432
DCOPClient::postMortemObject(),
433
DCOPClient::postMortemFunction());
436
void Kded::installCrashHandler()
438
KCrash::setEmergencySaveFunction(crashHandler);
441
void Kded::recreate()
446
void Kded::runDelayedCheck()
448
if( m_needDelayedCheck )
450
m_needDelayedCheck = false;
453
void Kded::recreate(bool initial)
455
m_recreateBusy = true;
456
// Using KLauncher here is difficult since we might not have a
461
updateDirWatch(); // Update tree first, to be sure to miss nothing.
462
runBuildSycoca(this, SLOT(recreateDone()));
467
updateDirWatch(); // this would search all the directories
472
// do a proper ksycoca check after a delay
473
QTimer::singleShot( 60000, this, SLOT( runDelayedCheck()));
474
m_needDelayedCheck = true;
475
delayedCheck = false;
478
m_needDelayedCheck = false;
482
void Kded::recreateDone()
484
updateResourceList();
486
for(; m_recreateCount; m_recreateCount--)
488
QCString replyType = "void";
489
QByteArray replyData;
490
DCOPClientTransaction *transaction = m_recreateRequests.first();
492
kapp->dcopClient()->endTransaction(transaction, replyType, replyData);
493
m_recreateRequests.remove(m_recreateRequests.begin());
495
m_recreateBusy = false;
497
// Did a new request come in while building?
498
if (!m_recreateRequests.isEmpty())
500
m_pTimer->start(2000, true /* single shot */ );
501
m_recreateCount = m_recreateRequests.count();
505
void Kded::dirDeleted(const QString& path)
510
void Kded::update(const QString& )
514
m_pTimer->start( 2000, true /* single shot */ );
518
m_recreateRequests.append(0);
522
bool Kded::process(const QCString &fun, const QByteArray &data,
523
QCString &replyType, QByteArray &replyData)
525
if (fun == "recreate()") {
528
if (m_recreateRequests.isEmpty())
530
m_pTimer->start(0, true /* single shot */ );
535
m_recreateRequests.append(kapp->dcopClient()->beginTransaction());
539
return DCOPObject::process(fun, data, replyType, replyData);
544
void Kded::readDirectory( const QString& _path )
546
QString path( _path );
547
if ( path.right(1) != "/" )
550
if ( m_pDirWatch->contains( path ) ) // Already seen this one?
553
QDir d( _path, QString::null, QDir::Unsorted, QDir::Readable | QDir::Executable | QDir::Dirs | QDir::Hidden );
557
//************************************************************************
559
//************************************************************************
561
m_pDirWatch->addDir(path); // add watch on this dir
563
if ( !d.exists() ) // exists&isdir?
565
kdDebug(7020) << QString("Does not exist! (%1)").arg(_path) << endl;
566
return; // return false
569
// Note: If some directory is gone, dirwatch will delete it from the list.
571
//************************************************************************
573
//************************************************************************
575
unsigned int i; // counter and string length.
576
unsigned int count = d.count();
577
for( i = 0; i < count; i++ ) // check all entries
579
if (d[i] == "." || d[i] == ".." || d[i] == "magic")
580
continue; // discard those ".", "..", "magic"...
582
file = path; // set full path
583
file += d[i]; // and add the file name.
585
readDirectory( file ); // yes, dive into it.
589
bool Kded::isWindowRegistered(long windowId)
591
return m_globalWindowIdList.find(windowId) != 0;
596
void Kded::registerWindowId(long windowId)
598
m_globalWindowIdList.replace(windowId, &windowId);
599
QCString sender = callingDcopClient()->senderId();
600
if( sender.isEmpty()) // local call
601
sender = callingDcopClient()->appId();
602
QValueList<long> *windowIds = m_windowIdList.find(sender);
605
windowIds = new QValueList<long>;
606
m_windowIdList.insert(sender, windowIds);
608
windowIds->append(windowId);
611
for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
613
emit it.current()->windowRegistered(windowId);
618
void Kded::unregisterWindowId(long windowId)
620
m_globalWindowIdList.remove(windowId);
621
QCString sender = callingDcopClient()->senderId();
622
if( sender.isEmpty()) // local call
623
sender = callingDcopClient()->appId();
624
QValueList<long> *windowIds = m_windowIdList.find(sender);
627
windowIds->remove(windowId);
628
if (windowIds->isEmpty())
629
m_windowIdList.remove(sender);
632
for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
634
emit it.current()->windowUnregistered(windowId);
639
static void sighandler(int /*sig*/)
647
m_pDirWatch = new KDirWatch;
648
m_pTimer = new QTimer;
649
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(runKonfUpdate()));
650
QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
651
this, SLOT(slotNewUpdateFile()));
653
QStringList dirs = KGlobal::dirs()->findDirs("data", "kconf_update");
654
for( QStringList::ConstIterator it = dirs.begin();
659
if (path[path.length()-1] != '/')
662
if (!m_pDirWatch->contains(path))
663
m_pDirWatch->addDir(path);
667
KUpdateD::~KUpdateD()
673
void KUpdateD::runKonfUpdate()
678
void KUpdateD::slotNewUpdateFile()
680
m_pTimer->start( 500, true /* single shot */ );
683
KHostnameD::KHostnameD(int pollInterval)
685
m_Timer.start(pollInterval, false /* repetitive */ );
686
connect(&m_Timer, SIGNAL(timeout()), this, SLOT(checkHostname()));
690
KHostnameD::~KHostnameD()
695
void KHostnameD::checkHostname()
698
if (gethostname(buf, 1024) != 0)
700
buf[sizeof(buf)-1] = '\0';
702
if (m_hostname.isEmpty())
708
if (m_hostname == buf)
711
QCString newHostname = buf;
713
runDontChangeHostname(m_hostname, newHostname);
714
m_hostname = newHostname;
718
static KCmdLineOptions options[] =
720
{ "check", I18N_NOOP("Check Sycoca database only once"), 0 },
721
{ "new-startup", "Internal", 0 },
725
class KDEDQtDCOPObject : public DCOPObject
728
KDEDQtDCOPObject() : DCOPObject("qt/kded") { }
730
virtual bool process(const QCString &fun, const QByteArray &data,
731
QCString& replyType, QByteArray &replyData)
733
if ( kapp && (fun == "quit()") )
739
return DCOPObject::process(fun, data, replyType, replyData);
742
QCStringList functions()
744
QCStringList res = DCOPObject::functions();
745
res += "void quit()";
750
class KDEDApplication : public KUniqueApplication
753
KDEDApplication() : KUniqueApplication( )
756
dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()",
757
objId(), "quit()", false );
764
if( Kded::self()->newStartup())
765
Kded::self()->initModules();
767
QTimer::singleShot(500, Kded::self(), SLOT(initModules()));
774
QCStringList functions()
776
QCStringList res = KUniqueApplication::functions();
777
res += "bool loadModule(QCString)";
778
res += "bool unloadModule(QCString)";
779
res += "void registerWindowId(long int)";
780
res += "void unregisterWindowId(long int)";
781
res += "QCStringList loadedModules()";
782
res += "void reconfigure()";
783
res += "void loadSecondPhase()";
784
res += "void quit()";
788
bool process(const QCString &fun, const QByteArray &data,
789
QCString &replyType, QByteArray &replyData)
791
if (fun == "loadModule(QCString)") {
793
QDataStream arg( data, IO_ReadOnly );
795
bool result = (Kded::self()->loadModule(module, false) != 0);
797
QDataStream _replyStream( replyData, IO_WriteOnly );
798
_replyStream << result;
801
else if (fun == "unloadModule(QCString)") {
803
QDataStream arg( data, IO_ReadOnly );
805
bool result = Kded::self()->unloadModule(module);
807
QDataStream _replyStream( replyData, IO_WriteOnly );
808
_replyStream << result;
811
else if (fun == "registerWindowId(long int)") {
813
QDataStream arg( data, IO_ReadOnly );
815
Kded::self()->setCallingDcopClient(callingDcopClient());
816
Kded::self()->registerWindowId(windowId);
820
else if (fun == "unregisterWindowId(long int)") {
822
QDataStream arg( data, IO_ReadOnly );
824
Kded::self()->setCallingDcopClient(callingDcopClient());
825
Kded::self()->unregisterWindowId(windowId);
829
else if (fun == "loadedModules()") {
830
replyType = "QCStringList";
831
QDataStream _replyStream(replyData, IO_WriteOnly);
832
_replyStream << Kded::self()->loadedModules();
835
else if (fun == "reconfigure()") {
836
config()->reparseConfiguration();
837
Kded::self()->initModules();
841
else if (fun == "loadSecondPhase()") {
842
Kded::self()->loadSecondPhase();
846
else if (fun == "quit()") {
851
return KUniqueApplication::process(fun, data, replyType, replyData);
855
KDEDQtDCOPObject kdedQtDcopObject;
858
extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
860
KAboutData aboutData( "kded", I18N_NOOP("KDE Daemon"),
861
"$Id: kded.cpp 711061 2007-09-11 09:42:51Z tpatzig $",
862
I18N_NOOP("KDE Daemon - triggers Sycoca database updates when needed"));
864
KApplication::installSigpipeHandler();
866
KCmdLineArgs::init(argc, argv, &aboutData);
868
KUniqueApplication::addCmdLineOptions();
870
KCmdLineArgs::addCmdLineOptions( options );
872
// this program is in kdelibs so it uses kdelibs as catalog
873
KLocale::setMainCatalogue("kdelibs");
875
// WABA: Make sure not to enable session management.
876
putenv(strdup("SESSION_MANAGER="));
878
// Parse command line before checking DCOP
879
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
881
// Check DCOP communication.
884
QCString dcopName = testDCOP.registerAs("kded", false);
885
if (dcopName.isEmpty())
887
kdFatal() << "DCOP communication problem!" << endl;
892
KInstance *instance = new KInstance(&aboutData);
893
KConfig *config = instance->config(); // Enable translations.
895
if (args->isSet("check"))
897
config->setGroup("General");
898
checkStamps = config->readBoolEntry("CheckFileStamps", true);
904
if (!KUniqueApplication::start())
906
fprintf(stderr, "KDE Daemon (kded) already running.\n");
910
KUniqueApplication::dcopClient()->setQtBridgeEnabled(false);
912
config->setGroup("General");
913
int HostnamePollInterval = config->readNumEntry("HostnamePollInterval", 5000);
914
bool bCheckSycoca = config->readBoolEntry("CheckSycoca", true);
915
bool bCheckUpdates = config->readBoolEntry("CheckUpdates", true);
916
bool bCheckHostname = config->readBoolEntry("CheckHostname", true);
917
checkStamps = config->readBoolEntry("CheckFileStamps", true);
918
delayedCheck = config->readBoolEntry("DelayedCheck", false);
920
Kded *kded = new Kded(bCheckSycoca, args->isSet("new-startup")); // Build data base
922
signal(SIGTERM, sighandler);
923
signal(SIGHUP, sighandler);
926
kded->recreate(true); // initial
929
(void) new KUpdateD; // Watch for updates
931
runKonfUpdate(); // Run it once.
934
(void) new KHostnameD(HostnamePollInterval); // Watch for hostname changes
936
DCOPClient *client = kapp->dcopClient();
937
QObject::connect(client, SIGNAL(applicationRemoved(const QCString&)),
938
kded, SLOT(slotApplicationRemoved(const QCString&)));
939
client->setNotifications(true);
940
client->setDaemonMode( true );
942
// During startup kdesktop waits for KDED to finish.
943
// Send a notifyDatabaseChanged signal even if the database hasn't
945
// If the database changed, kbuildsycoca's signal didn't go anywhere
946
// anyway, because it was too early, so let's send this signal
947
// unconditionnally (David)
949
client->send( "*", "ksycoca", "notifyDatabaseChanged()", data );
950
client->send( "ksplash", "", "upAndRunning(QString)", QString("kded"));
953
e.xclient.type = ClientMessage;
954
e.xclient.message_type = XInternAtom( qt_xdisplay(), "_KDE_SPLASH_PROGRESS", False );
955
e.xclient.display = qt_xdisplay();
956
e.xclient.window = qt_xrootwin();
957
e.xclient.format = 8;
958
strcpy( e.xclient.data.b, "kded" );
959
XSendEvent( qt_xdisplay(), qt_xrootwin(), False, SubstructureNotifyMask, &e );
961
int result = k.exec(); // keep running
964
delete instance; // Deletes config as well