File indexing completed on 2024-05-19 05:39:08

0001 /*
0002     SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include <config-workspace.h>
0008 
0009 #include "main.h"
0010 
0011 #include <unistd.h>
0012 
0013 #include <KFileUtils>
0014 #include <QDBusConnection>
0015 #include <QDBusMessage>
0016 #include <QDBusPendingCall>
0017 #include <QDebug>
0018 #include <QFile>
0019 #include <QGuiApplication>
0020 #include <QLibrary>
0021 #include <QPluginLoader>
0022 #include <QTimer>
0023 
0024 #include <KAboutData>
0025 #include <KConfig>
0026 #include <KConfigGroup>
0027 #include <KLocalizedString>
0028 
0029 static int ready[2];
0030 static bool startup = false;
0031 
0032 static void sendReady()
0033 {
0034     if (ready[1] == -1)
0035         return;
0036     char c = 0;
0037     write(ready[1], &c, 1);
0038     close(ready[1]);
0039     ready[1] = -1;
0040 }
0041 
0042 static void waitForReady()
0043 {
0044     char c = 1;
0045     close(ready[1]);
0046     read(ready[0], &c, 1);
0047     close(ready[0]);
0048 }
0049 
0050 bool KCMInit::runModule(const KPluginMetaData &data)
0051 {
0052     QString path = QPluginLoader(data.fileName()).fileName();
0053 
0054     // get the kcminit_ function
0055     QFunctionPointer init = QLibrary::resolve(path, "kcminit");
0056     if (!init) {
0057         qWarning() << "Module" << data.fileName() << "does not actually have a kcminit function";
0058         return false;
0059     }
0060 
0061     // initialize the module
0062     qDebug() << "Initializing " << data.fileName();
0063     init();
0064     return true;
0065 }
0066 
0067 void KCMInit::runModules(int phase)
0068 {
0069     for (const KPluginMetaData &data : std::as_const(m_list)) {
0070         // see ksmserver's README for the description of the phases
0071         int libphase = data.value(QStringLiteral("X-KDE-Init-Phase"), 1);
0072 
0073         if (libphase > 1) {
0074             libphase = 1;
0075         }
0076 
0077         if (phase != -1 && libphase != phase)
0078             continue;
0079 
0080         // try to load the library
0081         if (!m_alreadyInitialized.contains(data.pluginId())) {
0082             runModule(data);
0083             m_alreadyInitialized.append(data.pluginId());
0084         }
0085     }
0086 }
0087 
0088 KCMInit::KCMInit(const QCommandLineParser &args)
0089 {
0090     if (args.isSet(QStringLiteral("list"))) {
0091         m_list = KPluginMetaData::findPlugins(QStringLiteral("plasma/kcminit"));
0092         for (const KPluginMetaData &data : std::as_const(m_list)) {
0093             printf("%s\n", QFile::encodeName(data.fileName()).data());
0094         }
0095         return;
0096     }
0097 
0098     const auto positionalArguments = args.positionalArguments();
0099     if (!positionalArguments.isEmpty()) {
0100         for (const auto &arg : positionalArguments) {
0101             KPluginMetaData data(arg);
0102             if (!data.isValid()) {
0103                 data = KPluginMetaData::findPluginById(QStringLiteral("plasma/kcminit"), arg);
0104             }
0105 
0106             if (data.isValid()) {
0107                 m_list << data.fileName();
0108             } else {
0109                 qWarning() << "Could not find" << arg;
0110             }
0111         }
0112     } else {
0113         m_list = KPluginMetaData::findPlugins(QStringLiteral("plasma/kcminit"));
0114     }
0115 
0116     if (startup) {
0117         runModules(0);
0118         // Tell KSplash that KCMInit has started
0119         QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"),
0120                                                                              QStringLiteral("/KSplash"),
0121                                                                              QStringLiteral("org.kde.KSplash"),
0122                                                                              QStringLiteral("setStage"));
0123         ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("kcminit"));
0124         QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
0125 
0126         sendReady();
0127         QTimer::singleShot(300 * 1000, qApp, &QCoreApplication::quit); // just in case
0128 
0129         QDBusConnection::sessionBus().registerObject(QStringLiteral("/kcminit"), this, QDBusConnection::ExportScriptableContents);
0130         QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kcminit"));
0131 
0132         qApp->exec(); // wait for runPhase1()
0133     } else
0134         runModules(-1); // all phases
0135 }
0136 
0137 KCMInit::~KCMInit()
0138 {
0139     sendReady();
0140 }
0141 
0142 void KCMInit::runPhase1()
0143 {
0144     runModules(1);
0145     qApp->exit(0);
0146 }
0147 
0148 int main(int argc, char *argv[])
0149 {
0150     // plasma-session startup waits for kcminit to finish running phase 0 kcms
0151     // (theoretically that is only important kcms that need to be started very
0152     // early in the login process), the rest is delayed, so fork and make parent
0153     // return after the initial phase
0154     pipe(ready);
0155     if (fork() != 0) {
0156         waitForReady();
0157         return 0;
0158     }
0159     close(ready[0]);
0160 
0161     const QString executableName = QString::fromUtf8(argv[0]);
0162     startup = executableName.endsWith(QLatin1String("kcminit_startup")); // started from startkde?
0163 
0164     QGuiApplication::setDesktopSettingsAware(false);
0165     QGuiApplication app(argc, argv); // gui is needed for several modules
0166     KLocalizedString::setApplicationDomain(QByteArrayLiteral("kcminit"));
0167     KAboutData about(QStringLiteral("kcminit"),
0168                      i18n("KCMInit"),
0169                      QString(),
0170                      i18n("KCMInit - runs startup initialization for Control Modules."),
0171                      KAboutLicense::GPL);
0172     KAboutData::setApplicationData(about);
0173 
0174     QCommandLineParser parser;
0175     about.setupCommandLine(&parser);
0176     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("list"), i18n("List modules that are run at startup")));
0177     parser.addPositionalArgument(QStringLiteral("module"), i18n("Configuration module to run"));
0178 
0179     parser.process(app);
0180     about.processCommandLine(&parser);
0181 
0182     KCMInit kcminit(parser);
0183     return 0;
0184 }