File indexing completed on 2024-04-28 03:54:00

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kded.h"
0010 #include "kded_debug.h"
0011 #include "kded_version.h"
0012 #include "kdedadaptor.h"
0013 
0014 #include <KCrash>
0015 
0016 #include <qplatformdefs.h>
0017 
0018 #include <QApplication>
0019 #include <QCommandLineParser>
0020 #include <QDir>
0021 #include <QLoggingCategory>
0022 #include <QProcess>
0023 
0024 #include <QDBusConnection>
0025 #include <QDBusConnectionInterface>
0026 #include <QDBusServiceWatcher>
0027 
0028 #include <KConfigGroup>
0029 #include <KDBusService>
0030 #include <KDirWatch>
0031 #include <KPluginFactory>
0032 #include <KPluginMetaData>
0033 #include <KSharedConfig>
0034 
0035 #include <memory>
0036 
0037 Kded *Kded::_self = nullptr;
0038 
0039 static bool delayedCheck;
0040 static bool bCheckSycoca;
0041 static bool bCheckUpdates;
0042 
0043 #ifdef Q_DBUS_EXPORT
0044 extern Q_DBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
0045 #else
0046 extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
0047 #endif
0048 
0049 static void runKonfUpdate()
0050 {
0051     QProcess kconfUpdate;
0052     kconfUpdate.start(QStringLiteral(KCONF_UPDATE_EXE), QStringList());
0053     kconfUpdate.waitForFinished();
0054 }
0055 
0056 Kded::Kded()
0057     : m_pDirWatch(new KDirWatch(this))
0058     , m_pTimer(new QTimer(this))
0059     , m_needDelayedCheck(false)
0060 {
0061     _self = this;
0062 
0063     m_serviceWatcher = new QDBusServiceWatcher(this);
0064     m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
0065     m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
0066     QObject::connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &Kded::slotApplicationRemoved);
0067 
0068     new KdedAdaptor(this);
0069 
0070     QDBusConnection session = QDBusConnection::sessionBus();
0071     session.registerObject(QStringLiteral("/kbuildsycoca"), this);
0072     session.registerObject(QStringLiteral("/kded"), this);
0073 
0074     m_pTimer->setSingleShot(true);
0075     connect(m_pTimer, &QTimer::timeout, this, static_cast<void (Kded::*)()>(&Kded::recreate));
0076 }
0077 
0078 Kded::~Kded()
0079 {
0080     _self = nullptr;
0081     m_pTimer->stop();
0082 
0083     for (auto it = m_modules.cbegin(); it != m_modules.cend(); ++it) {
0084         delete *it;
0085     }
0086     m_modules.clear();
0087 }
0088 
0089 // on-demand module loading
0090 // this function is called by the D-Bus message processing function before
0091 // calls are delivered to objects
0092 void Kded::messageFilter(const QDBusMessage &message)
0093 {
0094     // This happens when kded goes down and some modules try to clean up.
0095     if (!self()) {
0096         return;
0097     }
0098 
0099     QString obj = KDEDModule::moduleForMessage(message);
0100     if (obj.isEmpty() || obj == QLatin1String("ksycoca")) {
0101         return;
0102     }
0103 
0104     if (self()->m_dontLoad.value(obj, nullptr)) {
0105         return;
0106     }
0107 
0108     self()->loadModule(obj, true);
0109 }
0110 
0111 static int phaseForModule(const KPluginMetaData &module)
0112 {
0113     return module.value(QStringLiteral("X-KDE-Kded-phase"), 2);
0114 }
0115 
0116 QList<KPluginMetaData> Kded::availableModules() const
0117 {
0118     QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kded"));
0119     QSet<QString> moduleIds;
0120     for (const KPluginMetaData &md : std::as_const(plugins)) {
0121         moduleIds.insert(md.pluginId());
0122     }
0123     return plugins;
0124 }
0125 
0126 static KPluginMetaData findModule(const QString &id)
0127 {
0128     KPluginMetaData module(QStringLiteral("kf6/kded/") + id);
0129     if (module.isValid()) {
0130         return module;
0131     }
0132     qCWarning(KDED) << "could not find kded module with id" << id;
0133     return KPluginMetaData();
0134 }
0135 
0136 void Kded::initModules()
0137 {
0138     m_dontLoad.clear();
0139 
0140     bool kde_running = !qEnvironmentVariableIsEmpty("KDE_FULL_SESSION");
0141     if (kde_running) {
0142         // not the same user like the one running the session (most likely we're run via sudo or something)
0143         const QByteArray sessionUID = qgetenv("KDE_SESSION_UID");
0144         if (!sessionUID.isEmpty() && uid_t(sessionUID.toInt()) != getuid()) {
0145             kde_running = false;
0146         }
0147         // not the same kde version as the current desktop
0148         const QByteArray kdeSession = qgetenv("KDE_SESSION_VERSION");
0149         if (kdeSession.toInt() != 6) {
0150             kde_running = false;
0151         }
0152     }
0153 
0154     // Preload kded modules.
0155     const QList<KPluginMetaData> kdedModules = availableModules();
0156     for (const KPluginMetaData &module : kdedModules) {
0157         // Should the service load on startup?
0158         const bool autoload = isModuleAutoloaded(module);
0159         if (!platformSupportsModule(module)) {
0160             continue;
0161         }
0162 
0163         // see ksmserver's README for description of the phases
0164         bool prevent_autoload = false;
0165         switch (phaseForModule(module)) {
0166         case 0: // always autoload
0167             break;
0168         case 1: // autoload only in KDE
0169             if (!kde_running) {
0170                 prevent_autoload = true;
0171             }
0172             break;
0173         case 2: // autoload delayed, only in KDE
0174         default:
0175             if (!kde_running) {
0176                 prevent_autoload = true;
0177             }
0178             break;
0179         }
0180 
0181         // Load the module if necessary and allowed
0182         if (autoload && !prevent_autoload) {
0183             if (!loadModule(module, false)) {
0184                 continue;
0185             }
0186         }
0187 
0188         // Remember if the module is allowed to load on demand
0189         bool loadOnDemand = isModuleLoadedOnDemand(module);
0190         if (!loadOnDemand) {
0191             noDemandLoad(module.pluginId());
0192         }
0193 
0194         // In case of reloading the configuration it is possible for a module
0195         // to run even if it is now allowed to. Stop it then.
0196         if (!loadOnDemand && !autoload) {
0197             unloadModule(module.pluginId());
0198         }
0199     }
0200 }
0201 
0202 void Kded::noDemandLoad(const QString &obj)
0203 {
0204     m_dontLoad.insert(obj, this);
0205 }
0206 
0207 void Kded::setModuleAutoloading(const QString &obj, bool autoload)
0208 {
0209     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
0210     // Ensure the service exists.
0211     KPluginMetaData module = findModule(obj);
0212     if (!module.isValid()) {
0213         return;
0214     }
0215     KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
0216     cg.writeEntry("autoload", autoload);
0217     cg.sync();
0218 }
0219 
0220 bool Kded::isModuleAutoloaded(const QString &obj) const
0221 {
0222     return isModuleAutoloaded(findModule(obj));
0223 }
0224 
0225 bool Kded::isModuleAutoloaded(const KPluginMetaData &module) const
0226 {
0227     if (!module.isValid()) {
0228         return false;
0229     }
0230     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
0231     bool autoload = module.value(QStringLiteral("X-KDE-Kded-autoload"), false);
0232     KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
0233     autoload = cg.readEntry("autoload", autoload);
0234     return autoload;
0235 }
0236 
0237 bool Kded::platformSupportsModule(const KPluginMetaData &module) const
0238 {
0239     const QStringList supportedPlatforms = module.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList());
0240 
0241     return supportedPlatforms.isEmpty() || supportedPlatforms.contains(qApp->platformName());
0242 }
0243 
0244 bool Kded::isModuleLoadedOnDemand(const QString &obj) const
0245 {
0246     return isModuleLoadedOnDemand(findModule(obj));
0247 }
0248 
0249 bool Kded::isModuleLoadedOnDemand(const KPluginMetaData &module) const
0250 {
0251     if (!module.isValid()) {
0252         return false;
0253     }
0254     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
0255     return module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true);
0256 }
0257 
0258 KDEDModule *Kded::loadModule(const QString &obj, bool onDemand)
0259 {
0260     // Make sure this method is only called with valid module names.
0261     if (obj.contains(QLatin1Char('/'))) {
0262         qCWarning(KDED) << "attempting to load invalid kded module name:" << obj;
0263         return nullptr;
0264     }
0265     KDEDModule *module = m_modules.value(obj, nullptr);
0266     if (module) {
0267         return module;
0268     }
0269     return loadModule(findModule(obj), onDemand);
0270 }
0271 
0272 KDEDModule *Kded::loadModule(const KPluginMetaData &module, bool onDemand)
0273 {
0274     if (!module.isValid() || module.fileName().isEmpty()) {
0275         qCWarning(KDED) << "attempted to load an invalid module.";
0276         return nullptr;
0277     }
0278     const QString moduleId = module.pluginId();
0279     KDEDModule *oldModule = m_modules.value(moduleId, nullptr);
0280     if (oldModule) {
0281         qCDebug(KDED) << "kded module" << moduleId << "is already loaded.";
0282         return oldModule;
0283     }
0284 
0285     if (onDemand) {
0286         if (!module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true)) {
0287             noDemandLoad(moduleId);
0288             return nullptr;
0289         }
0290     }
0291 
0292     KDEDModule *kdedModule = nullptr;
0293 
0294     auto factoryResult = KPluginFactory::loadFactory(module);
0295     if (factoryResult) {
0296         kdedModule = factoryResult.plugin->create<KDEDModule>(this);
0297     } else {
0298         qCWarning(KDED).nospace() << "Could not load kded module " << moduleId << ":" << factoryResult.errorText << " (library path was:" << module.fileName()
0299                                   << ")";
0300     }
0301 
0302     if (kdedModule) {
0303         kdedModule->setModuleName(moduleId);
0304         m_modules.insert(moduleId, kdedModule);
0305         // m_libs.insert(moduleId, lib);
0306         qCDebug(KDED) << "Successfully loaded module" << moduleId;
0307         return kdedModule;
0308     }
0309     return nullptr;
0310 }
0311 
0312 bool Kded::unloadModule(const QString &obj)
0313 {
0314     KDEDModule *module = m_modules.value(obj, nullptr);
0315     if (!module) {
0316         return false;
0317     }
0318     qCDebug(KDED) << "Unloading module" << obj;
0319     m_modules.remove(obj);
0320     delete module;
0321     return true;
0322 }
0323 
0324 QStringList Kded::loadedModules()
0325 {
0326     return m_modules.keys();
0327 }
0328 
0329 void Kded::slotApplicationRemoved(const QString &name)
0330 {
0331 #if 0 // see kdedmodule.cpp (KDED_OBJECTS)
0332     foreach (KDEDModule *module, m_modules) {
0333         module->removeAll(appId);
0334     }
0335 #endif
0336     m_serviceWatcher->removeWatchedService(name);
0337     const QList<qlonglong> windowIds = m_windowIdList.value(name);
0338     for (const auto id : windowIds) {
0339         m_globalWindowIdList.remove(id);
0340         for (KDEDModule *module : std::as_const(m_modules)) {
0341             Q_EMIT module->windowUnregistered(id);
0342         }
0343     }
0344     m_windowIdList.remove(name);
0345 }
0346 
0347 void Kded::updateDirWatch()
0348 {
0349     if (!bCheckUpdates) {
0350         return;
0351     }
0352 
0353     delete m_pDirWatch;
0354     m_pDirWatch = new KDirWatch(this);
0355 
0356     QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &Kded::update);
0357     QObject::connect(m_pDirWatch, &KDirWatch::created, this, &Kded::update);
0358     QObject::connect(m_pDirWatch, &KDirWatch::deleted, this, &Kded::dirDeleted);
0359 
0360     // For each resource
0361     for (const QString &dir : std::as_const(m_allResourceDirs)) {
0362         readDirectory(dir);
0363     }
0364 
0365     QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0366     for (auto &dir : dataDirs) {
0367         dir += QLatin1String("/icons");
0368         if (!m_pDirWatch->contains(dir)) {
0369             m_pDirWatch->addDir(dir, KDirWatch::WatchDirOnly);
0370         }
0371     }
0372 }
0373 
0374 void Kded::updateResourceList()
0375 {
0376     KSycoca::clearCaches();
0377 
0378     if (!bCheckUpdates) {
0379         return;
0380     }
0381 
0382     if (delayedCheck) {
0383         return;
0384     }
0385 
0386     const QStringList dirs = KSycoca::self()->allResourceDirs();
0387     // For each resource
0388     for (const auto &dir : dirs) {
0389         if (!m_allResourceDirs.contains(dir)) {
0390             m_allResourceDirs.append(dir);
0391             readDirectory(dir);
0392         }
0393     }
0394 }
0395 
0396 void Kded::recreate()
0397 {
0398     recreate(false);
0399 }
0400 
0401 void Kded::runDelayedCheck()
0402 {
0403     if (m_needDelayedCheck) {
0404         recreate(false);
0405     }
0406     m_needDelayedCheck = false;
0407 }
0408 
0409 void Kded::recreate(bool initial)
0410 {
0411     // Using KLauncher here is difficult since we might not have a
0412     // database
0413 
0414     if (!initial) {
0415         updateDirWatch(); // Update tree first, to be sure to miss nothing.
0416         KSycoca::self()->ensureCacheValid();
0417         recreateDone();
0418     } else {
0419         if (!delayedCheck) {
0420             updateDirWatch(); // this would search all the directories
0421         }
0422         if (bCheckSycoca) {
0423             KSycoca::self()->ensureCacheValid();
0424         }
0425         recreateDone();
0426         if (delayedCheck) {
0427             // do a proper ksycoca check after a delay
0428             QTimer::singleShot(60000, this, &Kded::runDelayedCheck);
0429             m_needDelayedCheck = true;
0430             delayedCheck = false;
0431         } else {
0432             m_needDelayedCheck = false;
0433         }
0434     }
0435 }
0436 
0437 void Kded::recreateDone()
0438 {
0439     updateResourceList();
0440 
0441     initModules();
0442 }
0443 
0444 void Kded::dirDeleted(const QString &path)
0445 {
0446     update(path);
0447 }
0448 
0449 void Kded::update(const QString &path)
0450 {
0451     if (path.endsWith(QLatin1String("/icons")) && m_pDirWatch->contains(path)) {
0452         // If the dir was created or updated there could be new folders to merge into the active theme(s)
0453         QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KIconLoader"), QStringLiteral("org.kde.KIconLoader"), QStringLiteral("iconChanged"));
0454         message << 0;
0455         QDBusConnection::sessionBus().send(message);
0456     } else {
0457         m_pTimer->start(1000);
0458     }
0459 }
0460 
0461 void Kded::readDirectory(const QString &_path)
0462 {
0463     QString path(_path);
0464     if (!path.endsWith(QLatin1Char('/'))) {
0465         path += QLatin1Char('/');
0466     }
0467 
0468     if (m_pDirWatch->contains(path)) { // Already seen this one?
0469         return;
0470     }
0471 
0472     Q_ASSERT(path != QDir::homePath());
0473     m_pDirWatch->addDir(path, KDirWatch::WatchFiles | KDirWatch::WatchSubDirs); // add watch on this dir
0474 }
0475 
0476 void Kded::registerWindowId(qlonglong windowId, const QString &sender)
0477 {
0478     if (!m_windowIdList.contains(sender)) {
0479         m_serviceWatcher->addWatchedService(sender);
0480     }
0481 
0482     m_globalWindowIdList.insert(windowId);
0483     QList<qlonglong> windowIds = m_windowIdList.value(sender);
0484     windowIds.append(windowId);
0485     m_windowIdList.insert(sender, windowIds);
0486 
0487     for (KDEDModule *module : std::as_const(m_modules)) {
0488         qCDebug(KDED) << module->moduleName();
0489         Q_EMIT module->windowRegistered(windowId);
0490     }
0491 }
0492 
0493 void Kded::unregisterWindowId(qlonglong windowId, const QString &sender)
0494 {
0495     m_globalWindowIdList.remove(windowId);
0496     QList<qlonglong> windowIds = m_windowIdList.value(sender);
0497     if (!windowIds.isEmpty()) {
0498         windowIds.removeAll(windowId);
0499         if (windowIds.isEmpty()) {
0500             m_serviceWatcher->removeWatchedService(sender);
0501             m_windowIdList.remove(sender);
0502         } else {
0503             m_windowIdList.insert(sender, windowIds);
0504         }
0505     }
0506 
0507     for (KDEDModule *module : std::as_const(m_modules)) {
0508         qCDebug(KDED) << module->moduleName();
0509         Q_EMIT module->windowUnregistered(windowId);
0510     }
0511 }
0512 
0513 static void sighandler(int /*sig*/)
0514 {
0515     if (qApp) {
0516         qApp->quit();
0517     }
0518 }
0519 
0520 KUpdateD::KUpdateD()
0521 {
0522     m_pDirWatch = new KDirWatch(this);
0523     m_pTimer = new QTimer(this);
0524     m_pTimer->setSingleShot(true);
0525     connect(m_pTimer, &QTimer::timeout, this, &KUpdateD::runKonfUpdate);
0526     QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &KUpdateD::slotNewUpdateFile);
0527 
0528     QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kconf_update"), QStandardPaths::LocateDirectory);
0529     for (auto &path : dirs) {
0530         Q_ASSERT(path != QDir::homePath());
0531         if (!path.endsWith(QLatin1Char('/'))) {
0532             path += QLatin1Char('/');
0533         }
0534 
0535         if (!m_pDirWatch->contains(path)) {
0536             m_pDirWatch->addDir(path, KDirWatch::WatchFiles);
0537         }
0538     }
0539 }
0540 
0541 KUpdateD::~KUpdateD()
0542 {
0543 }
0544 
0545 void KUpdateD::runKonfUpdate()
0546 {
0547     ::runKonfUpdate();
0548 }
0549 
0550 void KUpdateD::slotNewUpdateFile(const QString &dirty)
0551 {
0552     Q_UNUSED(dirty);
0553     qCDebug(KDED) << dirty;
0554     m_pTimer->start(500);
0555 }
0556 
0557 static void setupAppInfo(QApplication *app)
0558 {
0559     app->setApplicationName(QStringLiteral("kded6"));
0560     app->setOrganizationDomain(QStringLiteral("kde.org"));
0561     app->setApplicationVersion(QStringLiteral(KDED_VERSION_STRING));
0562 }
0563 
0564 static bool detectPlatform(int argc, char **argv)
0565 {
0566     if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
0567         return false;
0568     }
0569     for (int i = 0; i < argc; i++) {
0570         /* clang-format off */
0571         if (qstrcmp(argv[i], "-platform") == 0
0572             || qstrcmp(argv[i], "--platform") == 0
0573             || QByteArrayView(argv[i]).startsWith("-platform=")
0574             || QByteArrayView(argv[i]).startsWith("--platform=")) { /* clang-format on */
0575             return false;
0576         }
0577     }
0578     const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE");
0579     if (sessionType.isEmpty()) {
0580         return false;
0581     }
0582     if (qstrcmp(sessionType.data(), "wayland") == 0) {
0583         qputenv("QT_QPA_PLATFORM", "wayland");
0584         return true;
0585     } else if (qstrcmp(sessionType.data(), "x11") == 0) {
0586         qputenv("QT_QPA_PLATFORM", "xcb");
0587         return true;
0588     }
0589     return false;
0590 }
0591 
0592 int main(int argc, char *argv[])
0593 {
0594     // options.add("check", qi18n("Check Sycoca database only once"));
0595 
0596     // WABA: Make sure not to enable session management.
0597     qunsetenv("SESSION_MANAGER");
0598 
0599     const bool unsetQpa = detectPlatform(argc, argv);
0600 
0601     // In older versions, QApplication creation was postponed until after
0602     // testing for --check, in which case, only a QCoreApplication was created.
0603     // Since that option is no longer used at startup, we removed that speed
0604     // optimization for code clarity and easier support of standard parameters.
0605 
0606     QApplication app(argc, argv);
0607     if (unsetQpa) {
0608         qunsetenv("QT_QPA_PLATFORM");
0609     }
0610     setupAppInfo(&app);
0611     app.setQuitOnLastWindowClosed(false);
0612     app.setQuitLockEnabled(false);
0613 
0614     QCommandLineParser parser;
0615     parser.addHelpOption();
0616     parser.addVersionOption();
0617     parser.addOption(QCommandLineOption(QStringLiteral("check"), QStringLiteral("Check cache validity")));
0618     QCommandLineOption replaceOption({QStringLiteral("replace")}, QStringLiteral("Replace an existing instance"));
0619     parser.addOption(replaceOption);
0620     parser.process(app);
0621 
0622     // Parse command line before checking D-Bus
0623     if (parser.isSet(QStringLiteral("check"))) {
0624         // KDBusService not wanted here.
0625         KSycoca::self()->ensureCacheValid();
0626         runKonfUpdate();
0627         return 0;
0628     }
0629 
0630     // Qt now has DBus in another thread, so we need to handle any messages
0631     // between the service registration and our paths existing
0632     // This means adding the spy now, so any received message gets
0633     // posted to the main thread
0634     qDBusAddSpyHook(Kded::messageFilter);
0635 
0636     QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
0637     // Also register as all the names we should respond to (org.kde.kcookiejar, org.kde.khotkeys etc.)
0638     // so that the calling code is independent from the physical "location" of the service.
0639     const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kded"));
0640     for (const KPluginMetaData &metaData : plugins) {
0641         const QString serviceName = metaData.value(QStringLiteral("X-KDE-DBus-ServiceName"));
0642         if (serviceName.isEmpty()) {
0643             continue;
0644         }
0645         // register them queued as an old kded could be running at this point
0646         if (!bus->registerService(serviceName, QDBusConnectionInterface::QueueService)) {
0647             qCWarning(KDED) << "Couldn't register name" << serviceName << "with DBUS - another process owns it already!";
0648         }
0649     }
0650     KDBusService service(KDBusService::Unique | KDBusService::StartupOption(parser.isSet(replaceOption) ? KDBusService::Replace : 0));
0651 
0652     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
0653     KConfigGroup cg(config, QStringLiteral("General"));
0654 
0655     bCheckSycoca = cg.readEntry("CheckSycoca", true);
0656     bCheckUpdates = cg.readEntry("CheckUpdates", true);
0657     delayedCheck = cg.readEntry("DelayedCheck", false);
0658 
0659     signal(SIGTERM, sighandler);
0660     signal(SIGHUP, sighandler);
0661 
0662     KCrash::setFlags(KCrash::AutoRestart);
0663 
0664     std::unique_ptr<Kded> kded = std::make_unique<Kded>();
0665 
0666     kded->recreate(true); // initial
0667 
0668     if (bCheckUpdates) {
0669         (void)new KUpdateD; // Watch for updates
0670     }
0671 
0672     runKonfUpdate(); // Run it once.
0673 
0674     return app.exec(); // keep running
0675 }
0676 
0677 #include "moc_kded.cpp"