File indexing completed on 2024-05-19 05:39:09
0001 /* 0002 SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org> 0003 SPDX-FileCopyrightText: 2005 Lubos Lunak <l.lunak@kde.org> 0004 SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org> 0005 0006 SPDX-FileContributor: Oswald Buddenhagen <ob6@inf.tu-dresden.de> 0007 0008 some code taken from the dcopserver (part of the KDE libraries), which is 0009 SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org> 0010 SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org> 0011 0012 SPDX-License-Identifier: MIT 0013 */ 0014 0015 #include "startup.h" 0016 0017 #include "debug.h" 0018 0019 #include <unistd.h> 0020 0021 #include "kcminit_interface.h" 0022 #include "ksmserver_interface.h" 0023 0024 #include <KCompositeJob> 0025 #include <KConfig> 0026 #include <KConfigGroup> 0027 #include <KIO/ApplicationLauncherJob> 0028 #include <KProcess> 0029 #include <KService> 0030 0031 #include <QDBusConnection> 0032 #include <QDBusMessage> 0033 #include <QDBusPendingCall> 0034 #include <QDir> 0035 #include <QProcess> 0036 #include <QStandardPaths> 0037 #include <QTimer> 0038 0039 #include "sessiontrack.h" 0040 #include "startupadaptor.h" 0041 0042 #include "../config-startplasma.h" 0043 #include "startplasma.h" 0044 0045 class Phase : public KCompositeJob 0046 { 0047 Q_OBJECT 0048 public: 0049 Phase(const AutoStart &autostart, QObject *parent) 0050 : KCompositeJob(parent) 0051 , m_autostart(autostart) 0052 { 0053 } 0054 0055 bool addSubjob(KJob *job) override 0056 { 0057 bool rc = KCompositeJob::addSubjob(job); 0058 job->start(); 0059 return rc; 0060 } 0061 0062 void slotResult(KJob *job) override 0063 { 0064 KCompositeJob::slotResult(job); 0065 if (!hasSubjobs()) { 0066 emitResult(); 0067 } 0068 } 0069 0070 protected: 0071 const AutoStart m_autostart; 0072 }; 0073 0074 class StartupPhase0 : public Phase 0075 { 0076 Q_OBJECT 0077 public: 0078 StartupPhase0(const AutoStart &autostart, QObject *parent) 0079 : Phase(autostart, parent) 0080 { 0081 } 0082 void start() override 0083 { 0084 qCDebug(PLASMA_SESSION) << "Phase 0"; 0085 addSubjob(new AutoStartAppsJob(m_autostart, 0)); 0086 addSubjob(new KCMInitJob()); 0087 addSubjob(new SleepJob()); 0088 } 0089 }; 0090 0091 class StartupPhase1 : public Phase 0092 { 0093 Q_OBJECT 0094 public: 0095 StartupPhase1(const AutoStart &autostart, QObject *parent) 0096 : Phase(autostart, parent) 0097 { 0098 } 0099 void start() override 0100 { 0101 qCDebug(PLASMA_SESSION) << "Phase 1"; 0102 addSubjob(new AutoStartAppsJob(m_autostart, 1)); 0103 } 0104 }; 0105 0106 class StartupPhase2 : public Phase 0107 { 0108 Q_OBJECT 0109 public: 0110 StartupPhase2(const AutoStart &autostart, QObject *parent) 0111 : Phase(autostart, parent) 0112 { 0113 } 0114 0115 void start() override 0116 { 0117 qCDebug(PLASMA_SESSION) << "Phase 2"; 0118 addSubjob(new AutoStartAppsJob(m_autostart, 2)); 0119 } 0120 }; 0121 0122 SleepJob::SleepJob() 0123 { 0124 } 0125 0126 void SleepJob::start() 0127 { 0128 auto t = new QTimer(this); 0129 connect(t, &QTimer::timeout, this, [this]() { 0130 emitResult(); 0131 }); 0132 t->start(100); 0133 } 0134 0135 Startup::Startup(QObject *parent) 0136 : QObject(parent) 0137 { 0138 Q_ASSERT(!s_self); 0139 s_self = this; 0140 new StartupAdaptor(this); 0141 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Startup"), QStringLiteral("org.kde.Startup"), this); 0142 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Startup")); 0143 0144 const AutoStart autostart; 0145 0146 KJob *x11WindowManagerJob = nullptr; 0147 if (qEnvironmentVariable("XDG_SESSION_TYPE") != QLatin1String("wayland")) { 0148 QString windowManager; 0149 if (qEnvironmentVariableIsSet("KDEWM")) { 0150 windowManager = qEnvironmentVariable("KDEWM"); 0151 } 0152 if (windowManager.isEmpty()) { 0153 windowManager = QStringLiteral(KWIN_BIN); 0154 } 0155 0156 if (windowManager == QLatin1String(KWIN_BIN)) { 0157 x11WindowManagerJob = new StartServiceJob(windowManager, {}, QStringLiteral("org.kde.KWin")); 0158 } else { 0159 x11WindowManagerJob = new StartServiceJob(windowManager, {}, {}); 0160 } 0161 } else { 0162 // This must block until started as it sets the WAYLAND_DISPLAY/DISPLAY env variables needed for the rest of the boot 0163 // fortunately it's very fast as it's just starting a wrapper 0164 StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), {QStringLiteral("--xwayland")}, QStringLiteral("org.kde.KWinWrapper")); 0165 kwinWaylandJob.exec(); 0166 // kslpash is only launched in plasma-session from the wayland mode, for X it's in startplasma-x11 0167 0168 const KConfig cfg(QStringLiteral("ksplashrc")); 0169 // the splashscreen and progress indicator 0170 KConfigGroup ksplashCfg = cfg.group(QStringLiteral("KSplash")); 0171 if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) { 0172 QProcess::startDetached(QStringLiteral("ksplashqml"), {}); 0173 } 0174 } 0175 0176 // Keep for KF5; remove in KF6 (KInit will be gone then) 0177 QProcess::execute(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR_KF6 "/start_kdeinit_wrapper"), QStringList()); 0178 0179 KJob *phase1 = nullptr; 0180 m_lock.reset(new QEventLoopLocker); 0181 0182 const QList<KJob *> sequence = { 0183 new StartProcessJob(QStringLiteral("kcminit_startup"), {}), 0184 new StartServiceJob(QStringLiteral("kded6"), {}, QStringLiteral("org.kde.kded6"), {}), 0185 x11WindowManagerJob, 0186 new StartServiceJob(QStringLiteral("ksmserver"), QCoreApplication::instance()->arguments().mid(1), QStringLiteral("org.kde.ksmserver")), 0187 new StartupPhase0(autostart, this), 0188 phase1 = new StartupPhase1(autostart, this), 0189 new RestoreSessionJob(), 0190 new StartupPhase2(autostart, this), 0191 }; 0192 KJob *last = nullptr; 0193 for (KJob *job : sequence) { 0194 if (!job) { 0195 continue; 0196 } 0197 if (last) { 0198 connect(last, &KJob::finished, job, &KJob::start); 0199 } 0200 last = job; 0201 } 0202 0203 connect(sequence.last(), &KJob::finished, this, &Startup::finishStartup); 0204 sequence.first()->start(); 0205 0206 // app will be closed when all KJobs finish thanks to the QEventLoopLocker in each KJob 0207 } 0208 0209 void Startup::upAndRunning(const QString &msg) 0210 { 0211 QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), 0212 QStringLiteral("/KSplash"), 0213 QStringLiteral("org.kde.KSplash"), 0214 QStringLiteral("setStage")); 0215 ksplashProgressMessage.setArguments(QList<QVariant>() << msg); 0216 QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); 0217 } 0218 0219 void Startup::finishStartup() 0220 { 0221 qCDebug(PLASMA_SESSION) << "Finished"; 0222 upAndRunning(QStringLiteral("ready")); 0223 0224 playStartupSound(); 0225 new SessionTrack(m_processes); 0226 deleteLater(); 0227 } 0228 0229 void Startup::updateLaunchEnv(const QString &key, const QString &value) 0230 { 0231 qputenv(key.toLatin1(), value.toLatin1()); 0232 } 0233 0234 bool Startup::startDetached(QProcess *process) 0235 { 0236 process->setProcessChannelMode(QProcess::ForwardedChannels); 0237 process->start(); 0238 const bool ret = process->waitForStarted(); 0239 if (ret) { 0240 m_processes << process; 0241 } 0242 return ret; 0243 } 0244 0245 Startup *Startup::s_self = nullptr; 0246 0247 KCMInitJob::KCMInitJob() 0248 : KJob() 0249 { 0250 } 0251 0252 void KCMInitJob::start() 0253 { 0254 org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), QStringLiteral("/kcminit"), QDBusConnection::sessionBus()); 0255 kcminit.setTimeout(10 * 1000); 0256 0257 QDBusPendingReply<void> pending = kcminit.runPhase1(); 0258 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); 0259 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() { 0260 emitResult(); 0261 }); 0262 connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); 0263 } 0264 0265 RestoreSessionJob::RestoreSessionJob() 0266 : KJob() 0267 { 0268 } 0269 0270 void RestoreSessionJob::start() 0271 { 0272 OrgKdeKSMServerInterfaceInterface ksmserverIface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); 0273 auto pending = ksmserverIface.restoreSession(); 0274 0275 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); 0276 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() { 0277 emitResult(); 0278 }); 0279 connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); 0280 } 0281 0282 AutoStartAppsJob::AutoStartAppsJob(const AutoStart &autostart, int phase) 0283 : m_autoStart(autostart) 0284 { 0285 m_autoStart.setPhase(phase); 0286 } 0287 0288 void AutoStartAppsJob::start() 0289 { 0290 qCDebug(PLASMA_SESSION); 0291 0292 QTimer::singleShot(0, this, [this]() { 0293 do { 0294 QString serviceName = m_autoStart.startService(); 0295 if (serviceName.isEmpty()) { 0296 // Done 0297 if (!m_autoStart.phaseDone()) { 0298 m_autoStart.setPhaseDone(); 0299 } 0300 emitResult(); 0301 return; 0302 } 0303 auto job = new KIO::ApplicationLauncherJob(KService::Ptr(new KService(serviceName)), this); 0304 job->start(); 0305 } while (true); 0306 }); 0307 } 0308 0309 StartServiceJob::StartServiceJob(const QString &process, const QStringList &args, const QString &serviceId, const QProcessEnvironment &additionalEnv) 0310 : KJob() 0311 , m_process(new QProcess) 0312 , m_serviceId(serviceId) 0313 , m_additionalEnv(additionalEnv) 0314 { 0315 m_process->setProgram(process); 0316 m_process->setArguments(args); 0317 0318 auto watcher = new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration, this); 0319 connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &StartServiceJob::emitResult); 0320 } 0321 0322 void StartServiceJob::start() 0323 { 0324 auto env = QProcessEnvironment::systemEnvironment(); 0325 env.insert(m_additionalEnv); 0326 m_process->setProcessEnvironment(env); 0327 0328 if (!m_serviceId.isEmpty() && QDBusConnection::sessionBus().interface()->isServiceRegistered(m_serviceId)) { 0329 qCDebug(PLASMA_SESSION) << m_process << "already running"; 0330 emitResult(); 0331 return; 0332 } 0333 qCDebug(PLASMA_SESSION) << "Starting " << m_process->program() << m_process->arguments(); 0334 if (!Startup::self()->startDetached(m_process)) { 0335 qCWarning(PLASMA_SESSION) << "error starting process" << m_process->program() << m_process->arguments(); 0336 emitResult(); 0337 } 0338 0339 if (m_serviceId.isEmpty()) { 0340 emitResult(); 0341 } 0342 } 0343 0344 StartProcessJob::StartProcessJob(const QString &process, const QStringList &args, const QProcessEnvironment &additionalEnv) 0345 : KJob() 0346 , m_process(new QProcess(this)) 0347 { 0348 m_process->setProgram(process); 0349 m_process->setArguments(args); 0350 m_process->setProcessChannelMode(QProcess::ForwardedChannels); 0351 auto env = QProcessEnvironment::systemEnvironment(); 0352 env.insert(additionalEnv); 0353 m_process->setProcessEnvironment(env); 0354 0355 connect(m_process, &QProcess::finished, [this](int exitCode) { 0356 qCInfo(PLASMA_SESSION) << "process job " << m_process->program() << "finished with exit code " << exitCode; 0357 emitResult(); 0358 }); 0359 } 0360 0361 void StartProcessJob::start() 0362 { 0363 qCDebug(PLASMA_SESSION) << "Starting " << m_process->program() << m_process->arguments(); 0364 0365 m_process->start(); 0366 } 0367 0368 #include "startup.moc"