1 | // vim: expandtab sw=4 ts=4 |
2 | /* This file is part of the KDE libraries |
3 | * Copyright (C) 1999 David Faure <faure@kde.org> |
4 | * Copyright (C) 2000 Waldo Bastian <bastian@kde.org> |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License version 2 as published by the Free Software Foundation; |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | **/ |
20 | |
21 | #include "kded.h" |
22 | #include "kdedadaptor.h" |
23 | #include "kdedmodule.h" |
24 | |
25 | #include <kcrash.h> |
26 | #include <kdeversion.h> |
27 | |
28 | #include <unistd.h> |
29 | #include <stdlib.h> |
30 | #include <signal.h> |
31 | #include <time.h> |
32 | |
33 | #include <QtCore/QDir> |
34 | #include <QtCore/QFile> |
35 | #include <QtCore/QTimer> |
36 | |
37 | #include <QtDBus/QtDBus> |
38 | |
39 | #include <kuniqueapplication.h> |
40 | #include <kapplication.h> |
41 | #include <kcmdlineargs.h> |
42 | #include <kaboutdata.h> |
43 | #ifndef KDE_NO_DEPRECATED |
44 | #include <klibloader.h> |
45 | #else |
46 | #include <klibrary.h> |
47 | #endif |
48 | #include <klocale.h> |
49 | #include <kglobal.h> |
50 | #include <kconfig.h> |
51 | #include <kconfiggroup.h> |
52 | #include <kdebug.h> |
53 | #include <kdirwatch.h> |
54 | #include <kstandarddirs.h> |
55 | #include <kservicetypetrader.h> |
56 | #include <ktoolinvocation.h> |
57 | #include <kde_file.h> |
58 | #include "klauncher_iface.h" |
59 | |
60 | #ifdef Q_WS_X11 |
61 | #include <qx11info_x11.h> |
62 | #include <X11/Xlib.h> |
63 | #include <fixx11h.h> |
64 | #endif |
65 | |
66 | #define KDED_EXENAME "kded4" |
67 | |
68 | #define MODULES_PATH "/modules/" |
69 | |
70 | Kded *Kded::_self = 0; |
71 | |
72 | static bool checkStamps = true; |
73 | static bool delayedCheck = false; |
74 | static int HostnamePollInterval; |
75 | static bool bCheckSycoca; |
76 | static bool bCheckUpdates; |
77 | static bool bCheckHostname; |
78 | |
79 | #ifdef Q_DBUS_EXPORT |
80 | extern Q_DBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage&)); |
81 | #else |
82 | extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage&)); |
83 | #endif |
84 | |
85 | static void runBuildSycoca(QObject *callBackObj=0, const char *callBackSlot=0, const char *callBackErrorSlot=0) |
86 | { |
87 | const QString exe = KStandardDirs::findExe(KBUILDSYCOCA_EXENAME); |
88 | Q_ASSERT(!exe.isEmpty()); |
89 | QStringList args; |
90 | args.append("--incremental" ); |
91 | if(checkStamps) |
92 | args.append("--checkstamps" ); |
93 | if(delayedCheck) |
94 | args.append("--nocheckfiles" ); |
95 | else |
96 | checkStamps = false; // useful only during kded startup |
97 | if (callBackObj) |
98 | { |
99 | QVariantList argList; |
100 | argList << exe << args << QStringList() << QString(); |
101 | KToolInvocation::klauncher()->callWithCallback("kdeinit_exec_wait" , argList, callBackObj, callBackSlot, callBackErrorSlot); |
102 | } |
103 | else |
104 | { |
105 | KToolInvocation::kdeinitExecWait( exe, args ); |
106 | } |
107 | } |
108 | |
109 | static void runKonfUpdate() |
110 | { |
111 | KToolInvocation::kdeinitExecWait( "kconf_update" , QStringList(), 0, 0, "0" /*no startup notification*/ ); |
112 | } |
113 | |
114 | static void runDontChangeHostname(const QByteArray &oldName, const QByteArray &newName) |
115 | { |
116 | QStringList args; |
117 | args.append(QFile::decodeName(oldName)); |
118 | args.append(QFile::decodeName(newName)); |
119 | KToolInvocation::kdeinitExecWait( "kdontchangethehostname" , args ); |
120 | } |
121 | |
122 | Kded::Kded() |
123 | : m_needDelayedCheck(false) |
124 | { |
125 | _self = this; |
126 | |
127 | m_serviceWatcher = new QDBusServiceWatcher(this); |
128 | m_serviceWatcher->setConnection(QDBusConnection::sessionBus()); |
129 | m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); |
130 | QObject::connect(m_serviceWatcher, SIGNAL(serviceUnregistered(QString)), |
131 | this, SLOT(slotApplicationRemoved(QString))); |
132 | |
133 | new KBuildsycocaAdaptor(this); |
134 | new KdedAdaptor(this); |
135 | |
136 | QDBusConnection session = QDBusConnection::sessionBus(); |
137 | session.registerObject("/kbuildsycoca" , this); |
138 | session.registerObject("/kded" , this); |
139 | |
140 | qDBusAddSpyHook(messageFilter); |
141 | |
142 | m_pTimer = new QTimer(this); |
143 | m_pTimer->setSingleShot( true ); |
144 | connect(m_pTimer, SIGNAL(timeout()), this, SLOT(recreate())); |
145 | |
146 | m_pDirWatch = 0; |
147 | |
148 | m_recreateCount = 0; |
149 | m_recreateBusy = false; |
150 | } |
151 | |
152 | Kded::~Kded() |
153 | { |
154 | _self = 0; |
155 | m_pTimer->stop(); |
156 | delete m_pTimer; |
157 | delete m_pDirWatch; |
158 | |
159 | for (QHash<QByteArray,KDEDModule*>::iterator |
160 | it(m_modules.begin()), itEnd(m_modules.end()); |
161 | it != itEnd; ++it) |
162 | { |
163 | KDEDModule* module(it.value()); |
164 | |
165 | // first disconnect otherwise slotKDEDModuleRemoved() is called |
166 | // and changes m_modules while we're iterating over it |
167 | disconnect(module, SIGNAL(moduleDeleted(KDEDModule*)), |
168 | this, SLOT(slotKDEDModuleRemoved(KDEDModule*))); |
169 | |
170 | delete module; |
171 | } |
172 | } |
173 | |
174 | // on-demand module loading |
175 | // this function is called by the D-Bus message processing function before |
176 | // calls are delivered to objects |
177 | void Kded::messageFilter(const QDBusMessage &message) |
178 | { |
179 | // This happens when kded goes down and some modules try to clean up. |
180 | if (!self()) |
181 | return; |
182 | |
183 | if (message.type() != QDBusMessage::MethodCallMessage) |
184 | return; |
185 | |
186 | QString obj = message.path(); |
187 | if (!obj.startsWith(MODULES_PATH)) |
188 | return; |
189 | |
190 | // Remove the <MODULES_PATH> part |
191 | obj = obj.mid(strlen(MODULES_PATH)); |
192 | if (obj == "ksycoca" ) |
193 | return; // Ignore this one. |
194 | |
195 | // Remove the part after the modules name |
196 | int index = obj.indexOf('/'); |
197 | if (index!=-1) { |
198 | obj = obj.left(index); |
199 | } |
200 | |
201 | if (self()->m_dontLoad.value(obj, 0)) |
202 | return; |
203 | |
204 | KDEDModule *module = self()->loadModule(obj, true); |
205 | if (!module) { |
206 | kDebug(7020) << "Failed to load module for " << obj; |
207 | } |
208 | Q_UNUSED(module); |
209 | } |
210 | |
211 | static int phaseForModule(const KService::Ptr& service) |
212 | { |
213 | const QVariant phasev = service->property("X-KDE-Kded-phase" , QVariant::Int ); |
214 | return phasev.isValid() ? phasev.toInt() : 2; |
215 | } |
216 | |
217 | void Kded::initModules() |
218 | { |
219 | m_dontLoad.clear(); |
220 | bool kde_running = !qgetenv( "KDE_FULL_SESSION" ).isEmpty(); |
221 | if (kde_running) { |
222 | // not the same user like the one running the session (most likely we're run via sudo or something) |
223 | const QByteArray sessionUID = qgetenv( "KDE_SESSION_UID" ); |
224 | if( !sessionUID.isEmpty() && uid_t( sessionUID.toInt() ) != getuid()) |
225 | kde_running = false; |
226 | |
227 | // not the same kde version as the current desktop |
228 | const QByteArray kdeSession = qgetenv("KDE_SESSION_VERSION" ); |
229 | if (kdeSession.toInt() != KDE_VERSION_MAJOR) |
230 | kde_running = false; |
231 | } |
232 | |
233 | // There will be a "phase 2" only if we're in the KDE startup. |
234 | // If kded is restarted by its crashhandled or by hand, |
235 | // then there will be no second phase autoload, so load |
236 | // these modules now, if in a KDE session. |
237 | const bool loadPhase2Now = (kde_running && qgetenv("KDED_STARTED_BY_KDEINIT" ).toInt() == 0); |
238 | |
239 | // Preload kded modules. |
240 | const KService::List kdedModules = KServiceTypeTrader::self()->query("KDEDModule" ); |
241 | for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it) |
242 | { |
243 | KService::Ptr service = *it; |
244 | // Should the service load on startup? |
245 | const bool autoload = isModuleAutoloaded(service); |
246 | |
247 | // see ksmserver's README for description of the phases |
248 | bool prevent_autoload = false; |
249 | switch( phaseForModule(service) ) |
250 | { |
251 | case 0: // always autoload |
252 | break; |
253 | case 1: // autoload only in KDE |
254 | if (!kde_running) { |
255 | prevent_autoload = true; |
256 | } |
257 | break; |
258 | case 2: // autoload delayed, only in KDE |
259 | default: |
260 | if (!loadPhase2Now) { |
261 | prevent_autoload = true; |
262 | } |
263 | break; |
264 | } |
265 | |
266 | // Load the module if necessary and allowed |
267 | if (autoload && !prevent_autoload) { |
268 | if (!loadModule(service, false)) { |
269 | continue; |
270 | } |
271 | } |
272 | |
273 | // Remember if the module is allowed to load on demand |
274 | bool loadOnDemand = isModuleLoadedOnDemand(service); |
275 | if (!loadOnDemand) |
276 | noDemandLoad(service->desktopEntryName()); |
277 | |
278 | // In case of reloading the configuration it is possible for a module |
279 | // to run even if it is now allowed to. Stop it then. |
280 | if (!loadOnDemand && !autoload) |
281 | unloadModule(service->desktopEntryName().toLatin1()); |
282 | } |
283 | } |
284 | |
285 | void Kded::loadSecondPhase() |
286 | { |
287 | kDebug(7020) << "Loading second phase autoload" ; |
288 | KSharedConfig::Ptr config = KGlobal::config(); |
289 | KService::List kdedModules = KServiceTypeTrader::self()->query("KDEDModule" ); |
290 | for(KService::List::ConstIterator it = kdedModules.constBegin(); it != kdedModules.constEnd(); ++it) { |
291 | const KService::Ptr service = *it; |
292 | const bool autoload = isModuleAutoloaded(service); |
293 | if (autoload && phaseForModule(service) == 2) { |
294 | //kDebug(7020) << "2nd phase: loading" << service->desktopEntryName(); |
295 | loadModule(service, false); |
296 | } |
297 | } |
298 | } |
299 | |
300 | void Kded::noDemandLoad(const QString &obj) |
301 | { |
302 | m_dontLoad.insert(obj.toLatin1(), this); |
303 | } |
304 | |
305 | void Kded::setModuleAutoloading(const QString &obj, bool autoload) |
306 | { |
307 | KSharedConfig::Ptr config = KGlobal::config(); |
308 | // Ensure the service exists. |
309 | KService::Ptr service = KService::serviceByDesktopPath("kded/" +obj+".desktop" ); |
310 | if (!service) |
311 | return; |
312 | KConfigGroup cg(config, QString("Module-%1" ).arg(service->desktopEntryName())); |
313 | cg.writeEntry("autoload" , autoload); |
314 | cg.sync(); |
315 | } |
316 | |
317 | bool Kded::isModuleAutoloaded(const QString &obj) const |
318 | { |
319 | KService::Ptr s = KService::serviceByDesktopPath("kded/" +obj+".desktop" ); |
320 | if (!s) |
321 | return false; |
322 | return isModuleAutoloaded(s); |
323 | } |
324 | |
325 | bool Kded::isModuleAutoloaded(const KService::Ptr &module) const |
326 | { |
327 | KSharedConfig::Ptr config = KGlobal::config(); |
328 | bool autoload = module->property("X-KDE-Kded-autoload" , QVariant::Bool).toBool(); |
329 | KConfigGroup cg(config, QString("Module-%1" ).arg(module->desktopEntryName())); |
330 | autoload = cg.readEntry("autoload" , autoload); |
331 | return autoload; |
332 | } |
333 | |
334 | bool Kded::isModuleLoadedOnDemand(const QString &obj) const |
335 | { |
336 | KService::Ptr s = KService::serviceByDesktopPath("kded/" +obj+".desktop" ); |
337 | if (!s) |
338 | return false; |
339 | return isModuleLoadedOnDemand(s); |
340 | } |
341 | |
342 | bool Kded::isModuleLoadedOnDemand(const KService::Ptr &module) const |
343 | { |
344 | KSharedConfig::Ptr config = KGlobal::config(); |
345 | bool loadOnDemand = true; |
346 | QVariant p = module->property("X-KDE-Kded-load-on-demand" , QVariant::Bool); |
347 | if (p.isValid() && (p.toBool() == false)) |
348 | loadOnDemand = false; |
349 | return loadOnDemand; |
350 | } |
351 | |
352 | KDEDModule *Kded::loadModule(const QString &obj, bool onDemand) |
353 | { |
354 | // Make sure this method is only called with valid module names. |
355 | Q_ASSERT(obj.indexOf('/')==-1); |
356 | |
357 | KDEDModule *module = m_modules.value(obj, 0); |
358 | if (module) |
359 | return module; |
360 | KService::Ptr s = KService::serviceByDesktopPath("kded/" +obj+".desktop" ); |
361 | return loadModule(s, onDemand); |
362 | } |
363 | |
364 | KDEDModule *Kded::loadModule(const KService::Ptr& s, bool onDemand) |
365 | { |
366 | if (s && !s->library().isEmpty()) |
367 | { |
368 | QString obj = s->desktopEntryName(); |
369 | KDEDModule *oldModule = m_modules.value(obj, 0); |
370 | if (oldModule) |
371 | return oldModule; |
372 | |
373 | if (onDemand) |
374 | { |
375 | QVariant p = s->property("X-KDE-Kded-load-on-demand" , QVariant::Bool); |
376 | if (p.isValid() && (p.toBool() == false)) |
377 | { |
378 | noDemandLoad(s->desktopEntryName()); |
379 | return 0; |
380 | } |
381 | } |
382 | |
383 | KDEDModule *module = 0; |
384 | QString libname = "kded_" +s->library(); |
385 | KPluginLoader loader(libname); |
386 | |
387 | KPluginFactory *factory = loader.factory(); |
388 | if (!factory) { |
389 | // kde3 compat |
390 | QString factoryName = s->property("X-KDE-FactoryName" , QVariant::String).toString(); |
391 | if (factoryName.isEmpty()) |
392 | factoryName = s->library(); |
393 | factoryName = "create_" + factoryName; |
394 | #ifndef KDE_NO_DEPRECATED |
395 | KLibrary* lib = KLibLoader::self()->library(libname); |
396 | KDEDModule* (*create)(); |
397 | if (lib) { |
398 | create = (KDEDModule* (*)())lib->resolveFunction(QFile::encodeName(factoryName)); |
399 | if (create) |
400 | module = create(); |
401 | } |
402 | #endif |
403 | if (!module) { |
404 | kWarning() << "Could not load library" << libname << ". [" |
405 | << loader.errorString() << "]" ; |
406 | } |
407 | } else { |
408 | // create the module |
409 | module = factory->create<KDEDModule>(this); |
410 | } |
411 | if (module) { |
412 | module->setModuleName(obj); |
413 | m_modules.insert(obj, module); |
414 | //m_libs.insert(obj, lib); |
415 | connect(module, SIGNAL(moduleDeleted(KDEDModule*)), SLOT(slotKDEDModuleRemoved(KDEDModule*))); |
416 | kDebug(7020) << "Successfully loaded module" << obj; |
417 | return module; |
418 | } else { |
419 | kDebug(7020) << "Could not load module" << obj; |
420 | //loader.unload(); |
421 | } |
422 | } |
423 | return 0; |
424 | } |
425 | |
426 | bool Kded::unloadModule(const QString &obj) |
427 | { |
428 | KDEDModule *module = m_modules.value(obj, 0); |
429 | if (!module) |
430 | return false; |
431 | kDebug(7020) << "Unloading module" << obj; |
432 | m_modules.remove(obj); |
433 | delete module; |
434 | return true; |
435 | } |
436 | |
437 | QStringList Kded::loadedModules() |
438 | { |
439 | return m_modules.keys(); |
440 | } |
441 | |
442 | void Kded::slotKDEDModuleRemoved(KDEDModule *module) |
443 | { |
444 | m_modules.remove(module->moduleName()); |
445 | //KLibrary *lib = m_libs.take(module->moduleName()); |
446 | //if (lib) |
447 | // lib->unload(); |
448 | } |
449 | |
450 | void Kded::slotApplicationRemoved(const QString &name) |
451 | { |
452 | #if 0 // see kdedmodule.cpp (KDED_OBJECTS) |
453 | foreach( KDEDModule* module, m_modules ) |
454 | { |
455 | module->removeAll(appId); |
456 | } |
457 | #endif |
458 | m_serviceWatcher->removeWatchedService(name); |
459 | const QList<qlonglong> windowIds = m_windowIdList.value(name); |
460 | for( QList<qlonglong>::ConstIterator it = windowIds.begin(); |
461 | it != windowIds.end(); ++it) |
462 | { |
463 | qlonglong windowId = *it; |
464 | m_globalWindowIdList.remove(windowId); |
465 | foreach( KDEDModule* module, m_modules ) |
466 | { |
467 | emit module->windowUnregistered(windowId); |
468 | } |
469 | } |
470 | m_windowIdList.remove(name); |
471 | } |
472 | |
473 | void Kded::updateDirWatch() |
474 | { |
475 | if (!bCheckUpdates) return; |
476 | |
477 | delete m_pDirWatch; |
478 | m_pDirWatch = new KDirWatch; |
479 | |
480 | QObject::connect( m_pDirWatch, SIGNAL(dirty(QString)), |
481 | this, SLOT(update(QString))); |
482 | QObject::connect( m_pDirWatch, SIGNAL(created(QString)), |
483 | this, SLOT(update(QString))); |
484 | QObject::connect( m_pDirWatch, SIGNAL(deleted(QString)), |
485 | this, SLOT(dirDeleted(QString))); |
486 | |
487 | // For each resource |
488 | for( QStringList::ConstIterator it = m_allResourceDirs.constBegin(); |
489 | it != m_allResourceDirs.constEnd(); |
490 | ++it ) |
491 | { |
492 | readDirectory( *it ); |
493 | } |
494 | } |
495 | |
496 | void Kded::updateResourceList() |
497 | { |
498 | KSycoca::clearCaches(); |
499 | |
500 | if (!bCheckUpdates) return; |
501 | |
502 | if (delayedCheck) return; |
503 | |
504 | const QStringList dirs = KSycoca::self()->allResourceDirs(); |
505 | // For each resource |
506 | for( QStringList::ConstIterator it = dirs.begin(); |
507 | it != dirs.end(); |
508 | ++it ) |
509 | { |
510 | if (!m_allResourceDirs.contains(*it)) |
511 | { |
512 | m_allResourceDirs.append(*it); |
513 | readDirectory(*it); |
514 | } |
515 | } |
516 | } |
517 | |
518 | void Kded::recreate() |
519 | { |
520 | recreate(false); |
521 | } |
522 | |
523 | void Kded::runDelayedCheck() |
524 | { |
525 | if( m_needDelayedCheck ) |
526 | recreate(false); |
527 | m_needDelayedCheck = false; |
528 | } |
529 | |
530 | void Kded::recreate(bool initial) |
531 | { |
532 | m_recreateBusy = true; |
533 | // Using KLauncher here is difficult since we might not have a |
534 | // database |
535 | |
536 | if (!initial) |
537 | { |
538 | updateDirWatch(); // Update tree first, to be sure to miss nothing. |
539 | runBuildSycoca(this, SLOT(recreateDone()), SLOT(recreateFailed(QDBusError))); |
540 | } |
541 | else |
542 | { |
543 | if(!delayedCheck) |
544 | updateDirWatch(); // this would search all the directories |
545 | if (bCheckSycoca) |
546 | runBuildSycoca(); |
547 | recreateDone(); |
548 | if(delayedCheck) |
549 | { |
550 | // do a proper ksycoca check after a delay |
551 | QTimer::singleShot( 60000, this, SLOT(runDelayedCheck())); |
552 | m_needDelayedCheck = true; |
553 | delayedCheck = false; |
554 | } |
555 | else |
556 | m_needDelayedCheck = false; |
557 | } |
558 | } |
559 | |
560 | void Kded::recreateFailed(const QDBusError &error) |
561 | { |
562 | kWarning() << error; |
563 | for(; m_recreateCount; m_recreateCount--) |
564 | { |
565 | QDBusMessage msg = m_recreateRequests.takeFirst(); |
566 | QDBusConnection::sessionBus().send(msg.createErrorReply(error)); |
567 | } |
568 | afterRecreateFinished(); |
569 | } |
570 | |
571 | void Kded::recreateDone() |
572 | { |
573 | updateResourceList(); |
574 | |
575 | for(; m_recreateCount; m_recreateCount--) |
576 | { |
577 | QDBusMessage msg = m_recreateRequests.takeFirst(); |
578 | QDBusConnection::sessionBus().send(msg.createReply()); |
579 | } |
580 | afterRecreateFinished(); |
581 | } |
582 | |
583 | void Kded::afterRecreateFinished() |
584 | { |
585 | m_recreateBusy = false; |
586 | |
587 | // Did a new request come in while building? |
588 | if (!m_recreateRequests.isEmpty()) |
589 | { |
590 | m_pTimer->start(2000); |
591 | m_recreateCount = m_recreateRequests.count(); |
592 | } else { |
593 | initModules(); |
594 | } |
595 | } |
596 | |
597 | void Kded::dirDeleted(const QString& path) |
598 | { |
599 | update(path); |
600 | } |
601 | |
602 | void Kded::update(const QString& ) |
603 | { |
604 | if (!m_recreateBusy) |
605 | { |
606 | m_pTimer->start( 10000 ); |
607 | } |
608 | } |
609 | |
610 | void Kded::recreate(const QDBusMessage &msg) |
611 | { |
612 | if (!m_recreateBusy) |
613 | { |
614 | if (m_recreateRequests.isEmpty()) |
615 | { |
616 | m_pTimer->start(0); |
617 | m_recreateCount = 0; |
618 | } |
619 | m_recreateCount++; |
620 | } |
621 | msg.setDelayedReply(true); |
622 | m_recreateRequests.append(msg); |
623 | return; |
624 | } |
625 | |
626 | |
627 | void Kded::readDirectory( const QString& _path ) |
628 | { |
629 | QString path( _path ); |
630 | if ( !path.endsWith( '/' ) ) |
631 | path += '/'; |
632 | |
633 | if ( m_pDirWatch->contains( path ) ) // Already seen this one? |
634 | return; |
635 | |
636 | m_pDirWatch->addDir(path,KDirWatch::WatchFiles|KDirWatch::WatchSubDirs); // add watch on this dir |
637 | return; // KDirWatch now claims to also support recursive watching |
638 | #if 0 |
639 | QDir d( _path, QString(), QDir::Unsorted, QDir::Readable | QDir::Executable | QDir::Dirs | QDir::Hidden ); |
640 | // set QDir ... |
641 | |
642 | |
643 | //************************************************************************ |
644 | // Setting dirs |
645 | //************************************************************************ |
646 | |
647 | if ( !d.exists() ) // exists&isdir? |
648 | { |
649 | kDebug(7020) << "Does not exist:" << _path; |
650 | return; // return false |
651 | } |
652 | |
653 | // Note: If some directory is gone, dirwatch will delete it from the list. |
654 | |
655 | //************************************************************************ |
656 | // Reading |
657 | //************************************************************************ |
658 | QString file; |
659 | unsigned int i; // counter and string length. |
660 | unsigned int count = d.count(); |
661 | for( i = 0; i < count; i++ ) // check all entries |
662 | { |
663 | if (d[i] == "." || d[i] == ".." || d[i] == "magic" ) |
664 | continue; // discard those ".", "..", "magic"... |
665 | |
666 | file = path; // set full path |
667 | file += d[i]; // and add the file name. |
668 | |
669 | readDirectory( file ); // yes, dive into it. |
670 | } |
671 | #endif |
672 | } |
673 | |
674 | /* |
675 | bool Kded::isWindowRegistered(long windowId) const |
676 | { |
677 | return m_globalWindowIdList.contains(windowId); |
678 | |
679 | } |
680 | */ |
681 | |
682 | void Kded::registerWindowId(qlonglong windowId, const QString &sender) |
683 | { |
684 | if (!m_windowIdList.contains(sender)) { |
685 | m_serviceWatcher->addWatchedService(sender); |
686 | } |
687 | |
688 | m_globalWindowIdList.insert(windowId); |
689 | QList<qlonglong> windowIds = m_windowIdList.value(sender); |
690 | windowIds.append(windowId); |
691 | m_windowIdList.insert(sender, windowIds); |
692 | |
693 | foreach( KDEDModule* module, m_modules ) |
694 | { |
695 | //kDebug() << module->moduleName(); |
696 | emit module->windowRegistered(windowId); |
697 | } |
698 | } |
699 | |
700 | void Kded::unregisterWindowId(qlonglong windowId, const QString &sender) |
701 | { |
702 | m_globalWindowIdList.remove(windowId); |
703 | QList<qlonglong> windowIds = m_windowIdList.value(sender); |
704 | if (!windowIds.isEmpty()) |
705 | { |
706 | windowIds.removeAll(windowId); |
707 | if (windowIds.isEmpty()) { |
708 | m_serviceWatcher->removeWatchedService(sender); |
709 | m_windowIdList.remove(sender); |
710 | } else { |
711 | m_windowIdList.insert(sender, windowIds); |
712 | } |
713 | } |
714 | |
715 | foreach( KDEDModule* module, m_modules ) |
716 | { |
717 | //kDebug() << module->moduleName(); |
718 | emit module->windowUnregistered(windowId); |
719 | } |
720 | } |
721 | |
722 | |
723 | static void sighandler(int /*sig*/) |
724 | { |
725 | if (qApp) |
726 | qApp->quit(); |
727 | } |
728 | |
729 | KUpdateD::KUpdateD() |
730 | { |
731 | m_pDirWatch = new KDirWatch; |
732 | m_pTimer = new QTimer; |
733 | m_pTimer->setSingleShot( true ); |
734 | connect(m_pTimer, SIGNAL(timeout()), this, SLOT(runKonfUpdate())); |
735 | QObject::connect( m_pDirWatch, SIGNAL(dirty(QString)), |
736 | this, SLOT(slotNewUpdateFile())); |
737 | |
738 | const QStringList dirs = KGlobal::dirs()->findDirs("data" , "kconf_update" ); |
739 | for( QStringList::ConstIterator it = dirs.begin(); |
740 | it != dirs.end(); |
741 | ++it ) |
742 | { |
743 | QString path = *it; |
744 | if (path[path.length()-1] != '/') |
745 | path += '/'; |
746 | |
747 | if (!m_pDirWatch->contains(path)) |
748 | m_pDirWatch->addDir(path,KDirWatch::WatchFiles|KDirWatch::WatchSubDirs); |
749 | } |
750 | } |
751 | |
752 | KUpdateD::~KUpdateD() |
753 | { |
754 | delete m_pDirWatch; |
755 | delete m_pTimer; |
756 | } |
757 | |
758 | void KUpdateD::runKonfUpdate() |
759 | { |
760 | ::runKonfUpdate(); |
761 | } |
762 | |
763 | void KUpdateD::slotNewUpdateFile() |
764 | { |
765 | m_pTimer->start( 500 ); |
766 | } |
767 | |
768 | KHostnameD::KHostnameD(int pollInterval) |
769 | { |
770 | m_Timer.start(pollInterval); // repetitive timer (not single-shot) |
771 | connect(&m_Timer, SIGNAL(timeout()), this, SLOT(checkHostname())); |
772 | checkHostname(); |
773 | } |
774 | |
775 | KHostnameD::~KHostnameD() |
776 | { |
777 | // Empty |
778 | } |
779 | |
780 | void KHostnameD::checkHostname() |
781 | { |
782 | char buf[1024+1]; |
783 | if (gethostname(buf, 1024) != 0) |
784 | return; |
785 | buf[sizeof(buf)-1] = '\0'; |
786 | |
787 | if (m_hostname.isEmpty()) |
788 | { |
789 | m_hostname = buf; |
790 | return; |
791 | } |
792 | |
793 | if (m_hostname == buf) |
794 | return; |
795 | |
796 | QByteArray newHostname = buf; |
797 | |
798 | runDontChangeHostname(m_hostname, newHostname); |
799 | m_hostname = newHostname; |
800 | } |
801 | |
802 | |
803 | KBuildsycocaAdaptor::KBuildsycocaAdaptor(QObject *parent) |
804 | : QDBusAbstractAdaptor(parent) |
805 | { |
806 | } |
807 | |
808 | void KBuildsycocaAdaptor::recreate(const QDBusMessage &msg) |
809 | { |
810 | Kded::self()->recreate(msg); |
811 | } |
812 | |
813 | class KDEDApplication : public KUniqueApplication |
814 | { |
815 | public: |
816 | KDEDApplication() : KUniqueApplication( ) |
817 | { |
818 | startup = true; |
819 | } |
820 | |
821 | int newInstance() |
822 | { |
823 | if (startup) { |
824 | startup = false; |
825 | |
826 | // This long initialization has to be here, not in kdemain. |
827 | // If it was in main, it would cause a dbus timeout when |
828 | // our parent from KUniqueApplication tries to call our |
829 | // newInstance method. |
830 | |
831 | Kded *kded = Kded::self(); |
832 | |
833 | kded->recreate(true); // initial |
834 | |
835 | if (bCheckUpdates) |
836 | (void) new KUpdateD; // Watch for updates |
837 | |
838 | #ifdef Q_WS_X11 |
839 | XEvent e; |
840 | e.xclient.type = ClientMessage; |
841 | e.xclient.message_type = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS" , False ); |
842 | e.xclient.display = QX11Info::display(); |
843 | e.xclient.window = QX11Info::appRootWindow(); |
844 | e.xclient.format = 8; |
845 | strcpy( e.xclient.data.b, "kded" ); |
846 | XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False, SubstructureNotifyMask, &e ); |
847 | #endif |
848 | |
849 | runKonfUpdate(); // Run it once. |
850 | |
851 | #ifdef Q_WS_X11 |
852 | e.xclient.type = ClientMessage; |
853 | e.xclient.message_type = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS" , False ); |
854 | e.xclient.display = QX11Info::display(); |
855 | e.xclient.window = QX11Info::appRootWindow(); |
856 | e.xclient.format = 8; |
857 | strcpy( e.xclient.data.b, "confupdate" ); |
858 | XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False, SubstructureNotifyMask, &e ); |
859 | #endif |
860 | |
861 | // if (bCheckHostname) |
862 | // (void) new KHostnameD(HostnamePollInterval); // Watch for hostname changes |
863 | } else |
864 | runBuildSycoca(); |
865 | |
866 | return 0; |
867 | } |
868 | |
869 | bool startup; |
870 | }; |
871 | |
872 | extern "C" KDE_EXPORT int kdemain(int argc, char *argv[]) |
873 | { |
874 | KAboutData aboutData( "kded" /*don't change this one to kded4! dbus registration should be org.kde.kded etc.*/, |
875 | "kdelibs4" , ki18n("KDE Daemon" ), |
876 | KDE_VERSION_STRING, |
877 | ki18n("KDE Daemon - triggers Sycoca database updates when needed" )); |
878 | |
879 | KCmdLineOptions options; |
880 | options.add("check" , ki18n("Check Sycoca database only once" )); |
881 | |
882 | KCmdLineArgs::init(argc, argv, &aboutData); |
883 | |
884 | KUniqueApplication::addCmdLineOptions(); |
885 | |
886 | KCmdLineArgs::addCmdLineOptions( options ); |
887 | |
888 | // WABA: Make sure not to enable session management. |
889 | putenv(qstrdup("SESSION_MANAGER=" )); |
890 | |
891 | // Parse command line before checking DCOP |
892 | KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); |
893 | |
894 | KComponentData componentData(&aboutData); |
895 | KSharedConfig::Ptr config = componentData.config(); // Enable translations. |
896 | |
897 | KConfigGroup cg(config, "General" ); |
898 | if (args->isSet("check" )) |
899 | { |
900 | // KUniqueApplication not wanted here. |
901 | KApplication app; |
902 | checkStamps = cg.readEntry("CheckFileStamps" , true); |
903 | runBuildSycoca(); |
904 | runKonfUpdate(); |
905 | return 0; |
906 | } |
907 | |
908 | if (!KUniqueApplication::start()) |
909 | { |
910 | fprintf(stderr, "KDE Daemon (kded) already running.\n" ); |
911 | return 0; |
912 | } |
913 | |
914 | // Thiago: reenable if such a thing exists in QtDBus in the future |
915 | //KUniqueApplication::dcopClient()->setQtBridgeEnabled(false); |
916 | |
917 | HostnamePollInterval = cg.readEntry("HostnamePollInterval" , 5000); |
918 | bCheckSycoca = cg.readEntry("CheckSycoca" , true); |
919 | bCheckUpdates = cg.readEntry("CheckUpdates" , true); |
920 | bCheckHostname = cg.readEntry("CheckHostname" , true); |
921 | checkStamps = cg.readEntry("CheckFileStamps" , true); |
922 | delayedCheck = cg.readEntry("DelayedCheck" , false); |
923 | |
924 | Kded *kded = new Kded(); // Build data base |
925 | |
926 | #ifndef _WIN32_WCE |
927 | KDE_signal(SIGTERM, sighandler); |
928 | #endif |
929 | KDE_signal(SIGHUP, sighandler); |
930 | KDEDApplication k; |
931 | k.setQuitOnLastWindowClosed(false); |
932 | |
933 | KCrash::setFlags(KCrash::AutoRestart); |
934 | |
935 | // Not sure why kded is created before KDEDApplication |
936 | // but if it has to be, then it needs to be moved to the main thread |
937 | // before it can use timers (DF) |
938 | kded->moveToThread( k.thread() ); |
939 | |
940 | int result = k.exec(); // keep running |
941 | |
942 | delete kded; |
943 | |
944 | return result; |
945 | } |
946 | |
947 | #include "kded.moc" |
948 | |