From aa6b5ea4f005d77023fdf741bf743137101cd184 Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Sat, 3 Dec 2022 06:47:32 +0200 Subject: [PATCH] kinit: klauncher and KCrash rewrite much simpler and easier to maintain, also it will be proper D-Bus service now with no sockets, process title hack and whatnot. KCrash and startkde script (in kde-workspace repo) have to be adjusted for it tho note that the internal KIO scheduler already has the functionality to put slaves on hold but now they will be started as detached process. eventually they may become plugins (they are loaded as such by kioslave program) without event loop dispatcher fixes application startup notification (ASN) and cleanup issues Signed-off-by: Ivailo Monev --- ConfigureChecks.cmake | 2 - kdecore/kdebug.areas | 2 +- kdecore/kernel/ktoolinvocation.cpp | 19 +- kdecore/tests/kstandarddirstest.cpp | 20 +- kdecore/util/klauncher_iface.h | 32 - kded/kbuildsycoca.cpp | 20 +- kdeui/kernel/kapplication.cpp | 8 +- kdeui/kernel/kapplication.h | 5 - kdeui/util/kcrash.cpp | 771 ++------------ kdeui/util/kcrash.h | 189 ++-- kinit/CMakeLists.txt | 62 +- kinit/autostart.cpp | 122 --- kinit/autostart.h | 48 - kinit/config-kdeinit.h.cmake | 5 - kinit/kinit.cpp | 1533 --------------------------- kinit/klauncher.cpp | 1254 ++-------------------- kinit/klauncher.h | 262 +---- kinit/klauncher_adaptor.cpp | 392 +++++-- kinit/klauncher_adaptor.h | 92 +- kinit/klauncher_cmds.cpp | 45 - kinit/klauncher_cmds.h | 127 --- kinit/klauncher_main.cpp | 118 --- kinit/org.kde.klauncher.service.in | 3 + kinit/proctitle.cpp | 89 -- kinit/proctitle.h | 47 - kio/kio/slave.cpp | 89 +- kio/tests/krununittest.cpp | 38 +- 27 files changed, 704 insertions(+), 4690 deletions(-) delete mode 100644 kinit/autostart.cpp delete mode 100644 kinit/autostart.h delete mode 100644 kinit/config-kdeinit.h.cmake delete mode 100644 kinit/kinit.cpp delete mode 100644 kinit/klauncher_cmds.cpp delete mode 100644 kinit/klauncher_cmds.h delete mode 100644 kinit/klauncher_main.cpp create mode 100644 kinit/org.kde.klauncher.service.in delete mode 100644 kinit/proctitle.cpp delete mode 100644 kinit/proctitle.h diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 31e93f63..09eaad15 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -57,8 +57,6 @@ check_function_exists(fdatasync HAVE_FDATASYNC) # kd check_function_exists(arc4random_uniform HAVE_ARC4RANDOM_UNIFORM) # kdecore check_function_exists(sendfile HAVE_SENDFILE) # kioslave -check_library_exists(socket connect "" HAVE_SOCKET_LIBRARY) # kinit - if (UNIX) # for kpty check_include_files("sys/types.h;libutil.h" HAVE_LIBUTIL_H) diff --git a/kdecore/kdebug.areas b/kdecore/kdebug.areas index 0187bbd6..13e0c57d 100644 --- a/kdecore/kdebug.areas +++ b/kdecore/kdebug.areas @@ -42,6 +42,7 @@ 940 kmediaplayer 1000 kparts 1209 libplasma +1210 klauncher4 7000 kio 7001 kio (KDirWatch) 7002 kio (Slave) @@ -54,7 +55,6 @@ 7011 kdecore (KSycoca) 7014 kdecore (trader) 7015 kio (KAutoMount) -7016 kio (KLauncher) 7017 kio (KIOConnection) 7019 kio (kioslave) 7020 kded4 diff --git a/kdecore/kernel/ktoolinvocation.cpp b/kdecore/kernel/ktoolinvocation.cpp index 87340b6d..f8213b30 100644 --- a/kdecore/kernel/ktoolinvocation.cpp +++ b/kdecore/kernel/ktoolinvocation.cpp @@ -59,18 +59,13 @@ KToolInvocation::~KToolInvocation() org::kde::KLauncher *KToolInvocation::klauncher() { - if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QString::fromLatin1("org.kde.klauncher"))) { - kDebug(180) << "klauncher not running... launching kdeinit"; - // Try to launch kdeinit. - QString srv = KStandardDirs::findExe(QLatin1String("kdeinit4")); - if (srv.isEmpty()) { - kError() << "kdeinit4 not available"; - // NOTE: this will crash users not checking the pointer - return nullptr; - } - QStringList args; - args += QString::fromLatin1("--suicide"); - QProcess::execute(srv, args); + // If klauncher is not running we need to launch it + static const QString klauncherInterface = QString::fromLatin1("org.kde.klauncher"); + QDBusConnectionInterface* sessionInterface = QDBusConnection::sessionBus().interface(); + const bool klauncherRunning = sessionInterface->isServiceRegistered(klauncherInterface); + if (!klauncherRunning) { + kDebug(7011) << "Launching klauncher"; + sessionInterface->startService(klauncherInterface); } return self()->klauncherIface; } diff --git a/kdecore/tests/kstandarddirstest.cpp b/kdecore/tests/kstandarddirstest.cpp index 3b650621..fd754c5e 100644 --- a/kdecore/tests/kstandarddirstest.cpp +++ b/kdecore/tests/kstandarddirstest.cpp @@ -205,23 +205,23 @@ void KStandarddirsTest::testFindExe() QSKIP( "kdelibs not installed", SkipAll ); // findExe with a result in bin - const QString kdeinit = KGlobal::dirs()->findExe( "kdeinit4" ); - QVERIFY( !kdeinit.isEmpty() ); - QVERIFY2(kdeinit.endsWith("bin/kdeinit4" EXT, PATH_SENSITIVITY), qPrintable(kdeinit)); + const QString klauncher = KGlobal::dirs()->findExe( "klauncher4" ); + QVERIFY( !klauncher.isEmpty() ); + QVERIFY2(klauncher.endsWith("bin/klauncher4" EXT, PATH_SENSITIVITY), qPrintable(klauncher)); // Check the "exe" resource too - QString kdeinitexe = KGlobal::dirs()->locate( "exe", "kdeinit4" ); - QVERIFY2(kdeinitexe.endsWith("bin/kdeinit4" EXT, PATH_SENSITIVITY), qPrintable(kdeinit)); + QString klauncherexe = KGlobal::dirs()->locate( "exe", "klauncher4" ); + QVERIFY2(klauncherexe.endsWith("bin/klauncher4" EXT, PATH_SENSITIVITY), qPrintable(klauncherexe)); #ifdef Q_OS_UNIX // findExe with a result in libexec - const QString klauncher = KGlobal::dirs()->findExe( "klauncher" ); - QVERIFY( !klauncher.isEmpty() ); - QVERIFY( klauncher.endsWith("/klauncher" EXT, PATH_SENSITIVITY ) ); + const QString kioslave = KGlobal::dirs()->findExe( "kioslave" ); + QVERIFY( !kioslave.isEmpty() ); + QVERIFY( kioslave.endsWith("/kioslave" EXT, PATH_SENSITIVITY ) ); // locate("exe") with a result in libexec - const QString locateExeResult = KGlobal::dirs()->locate("exe", "klauncher"); - QVERIFY(locateExeResult.endsWith("/klauncher" EXT, PATH_SENSITIVITY)); + const QString locateExeResult = KGlobal::dirs()->locate("exe", "kioslave"); + QVERIFY(locateExeResult.endsWith("/kioslave" EXT, PATH_SENSITIVITY)); // findExe with relative path const QString pwd = QDir::currentPath(); diff --git a/kdecore/util/klauncher_iface.h b/kdecore/util/klauncher_iface.h index 5060f35e..94897dfd 100644 --- a/kdecore/util/klauncher_iface.h +++ b/kdecore/util/klauncher_iface.h @@ -100,24 +100,6 @@ public Q_SLOTS: // METHODS return callWithArgumentList(QDBus::Block, QLatin1String("reparseConfiguration"), argumentList); } - inline QDBusReply requestHoldSlave(const QString &url, const QString &app_socket) - { - QList argumentList; - argumentList << qVariantFromValue(url) << qVariantFromValue(app_socket); - return callWithArgumentList(QDBus::Block, QLatin1String("requestHoldSlave"), argumentList); - } - - inline QDBusReply requestSlave(const QString &protocol, const QString &host, const QString &app_socket, QString &error) - { - QList argumentList; - argumentList << qVariantFromValue(protocol) << qVariantFromValue(host) << qVariantFromValue(app_socket); - QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("requestSlave"), argumentList); - if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) { - error = qdbus_cast(reply.arguments().at(1)); - } - return reply; - } - inline QDBusReply setLaunchEnv(const QString &name, const QString &value) { QList argumentList; @@ -151,20 +133,6 @@ public Q_SLOTS: // METHODS return reply; } - inline QDBusReply waitForSlave(int pid) - { - QList argumentList; - argumentList << qVariantFromValue(pid); - return callWithArgumentList(QDBus::Block, QLatin1String("waitForSlave"), argumentList); - } - - inline QDBusReply checkForHeldSlave(const QString &url) - { - QList argumentList; - argumentList << qVariantFromValue(url); - return callWithArgumentList(QDBus::Block, QLatin1String("checkForHeldSlave"), argumentList); - } - Q_SIGNALS: // SIGNALS void autoStart0Done(); void autoStart1Done(); diff --git a/kded/kbuildsycoca.cpp b/kded/kbuildsycoca.cpp index 16295ac1..88908b82 100644 --- a/kded/kbuildsycoca.cpp +++ b/kded/kbuildsycoca.cpp @@ -51,12 +51,14 @@ #include #include #ifndef KBUILDSYCOCA_NO_KCRASH +#include #include #endif #include #include #include +#include typedef QHash KBSEntryDict; typedef QList KSycocaEntryListList; @@ -81,12 +83,16 @@ static QByteArray g_sycocaPath = 0; static bool bGlobalDatabase = false; static bool bMenuTest = false; -void crashHandler(int) +void crashHandler(int sig) { - // If we crash while reading sycoca, we delete the database - // in an attempt to recover. - if (!g_sycocaPath.isEmpty()) - unlink(g_sycocaPath.constData()); + KDE_signal(sig, SIG_DFL); + + // If we crash while reading sycoca, we delete the database in an attempt to recover. + if (!g_sycocaPath.isEmpty()) { + unlink(g_sycocaPath.constData()); + } + + ::exit(sig); } static QString sycocaPath() @@ -641,9 +647,7 @@ int main(int argc, char **argv) KComponentData mainComponent(d); #ifndef KBUILDSYCOCA_NO_KCRASH - KCrash::setCrashHandler(KCrash::defaultCrashHandler); - KCrash::setEmergencySaveFunction(crashHandler); - KCrash::setApplicationName(QString(appName)); + KCrash::setCrashHandler(crashHandler); #endif // force generating of KLocale object. if not, the database will get diff --git a/kdeui/kernel/kapplication.cpp b/kdeui/kernel/kapplication.cpp index eb30e674..68de5b74 100644 --- a/kdeui/kernel/kapplication.cpp +++ b/kdeui/kernel/kapplication.cpp @@ -79,7 +79,6 @@ #endif KApplication* KApplication::KApp = 0L; -bool KApplication::loadedByKdeinit = false; #ifdef Q_WS_X11 static Atom atom_DesktopWindow = None; @@ -666,12 +665,7 @@ void KApplicationPrivate::parseCommandLine( ) if (qgetenv("KDE_DEBUG").isEmpty() && args->isSet("crashhandler")) { // enable drkonqi - KCrash::setDrKonqiEnabled(true); - } - // Always set the app name, can be usefuls for apps that call setEmergencySaveFunction or enable AutoRestart - KCrash::setApplicationName(args->appName()); - if (!QCoreApplication::applicationDirPath().isEmpty()) { - KCrash::setApplicationPath(QCoreApplication::applicationDirPath()); + KCrash::setFlags(KCrash::flags() | KCrash::DrKonqi); } #ifdef Q_WS_X11 diff --git a/kdeui/kernel/kapplication.h b/kdeui/kernel/kapplication.h index d6d5bd9b..8236e180 100644 --- a/kdeui/kernel/kapplication.h +++ b/kdeui/kernel/kapplication.h @@ -263,11 +263,6 @@ public: bool notify( QObject* receiver, QEvent* event ); #endif // Q_WS_X11 - /** - * @internal - */ - static bool loadedByKdeinit; - public Q_SLOTS: /** * Updates the last user action timestamp to the given time, or to the current time, diff --git a/kdeui/util/kcrash.cpp b/kdeui/util/kcrash.cpp index 9fd9f9eb..97079be0 100644 --- a/kdeui/util/kcrash.cpp +++ b/kdeui/util/kcrash.cpp @@ -1,710 +1,157 @@ -/* - * This file is part of the KDE Libraries - * Copyright (C) 2000 Timo Hummel - * Tom Braun - * Copyright 2009 KDE e.V. - * By Adriaan de Groot - * Copyright (C) 2010 George Kiagiadakis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ #include "kcrash.h" -#include -#include -#include +#include "kcmdlineargs.h" +#include "kde_file.h" +#include "kaboutdata.h" +#include "kcomponentdata.h" +#include "kstandarddirs.h" +#include "kdebug.h" -#include +#include +#include +#include -#include #include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#ifdef Q_OS_LINUX -#include -#endif -#include - -#include -#include -#include -#include -#include -#include -#include - -#include <../kinit/klauncher_cmds.h> - -#include -#include - -#if defined Q_WS_X11 -#include #include -#endif +#include "fixx11h.h" -#ifdef Q_OS_SOLARIS -// Solaris has built-in, thread-safe, async-signal-safe, mechanisms -// to walk the stack in the case of a crash, as well as (optionally) -// to demangle C++ symbol names. In the case of a crash, dump a stack -// trace to stderr before starting drKonqui (because what drKonqui is -// going to do is -- through a complicated process -- print the -// exact same information, but less reliably). -#include -#endif - - -static KCrash::HandlerType s_emergencySaveFunction = 0; -static KCrash::HandlerType s_crashHandler = 0; -static char *s_appName = 0; -static char *s_autoRestartCommand = 0; -static char *s_appPath = 0; -static int s_autoRestartArgc = 0; -static char **s_autoRestartCommandLine = 0; -static char *s_drkonqiPath = 0; +static KCrash::HandlerType s_crashHandler = nullptr; static KCrash::CrashFlags s_flags = 0; -static bool s_launchDrKonqi = false; - -namespace KCrash -{ - void startProcess(int argc, const char *argv[], bool waitAndExit); - -} - -void -KCrash::setEmergencySaveFunction (HandlerType saveFunction) -{ - s_emergencySaveFunction = saveFunction; - - /* - * We need at least the default crash handler for - * emergencySaveFunction to be called - */ - if (s_emergencySaveFunction && !s_crashHandler) { - setCrashHandler(defaultCrashHandler); - } -} - -KCrash::HandlerType -KCrash::emergencySaveFunction() -{ - return s_emergencySaveFunction; -} - -// Set the default crash handler in 10 seconds -// This is used after an autorestart, the second instance of the application -// is started with --nocrashhandler (no drkonqi, more precisely), and we -// set the defaultCrashHandler (to handle autorestart) after 10s. -// The delay is to see if we stay up for more than 10s time, to avoid infinite -// respawning if the app crashes on startup. -class KCrashDelaySetHandler : public QObject -{ -public: - KCrashDelaySetHandler() { - startTimer(10000); // 10 s - } -protected: - void timerEvent(QTimerEvent *event) { - if (!s_crashHandler) // not set meanwhile - KCrash::setCrashHandler(KCrash::defaultCrashHandler); - killTimer(event->timerId()); - this->deleteLater(); - } -}; - - - -void -KCrash::setFlags(KCrash::CrashFlags flags) -{ - s_flags = flags; - if (s_flags & AutoRestart) { - // We need at least the default crash handler for autorestart to work. - if (!s_crashHandler) { - KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); - if (!args->isSet("crashhandler")) // --nocrashhandler was passed, probably due to a crash, delay restart handler - new KCrashDelaySetHandler; - else // probably because KDE_DEBUG=1. set restart handler immediately. - setCrashHandler(defaultCrashHandler); - } - } -} - -//### KDE5:Consider merging the setApplicationPath and setApplicationName methods into one. -void -KCrash::setApplicationPath(const QString& path) -{ - s_appPath = qstrdup(QFile::encodeName(path).constData()); - - //if the appName has also been specified, update s_autoRestartCommand to be in the form "absolutePath/appName" - if (s_appName) { - delete[] s_autoRestartCommand; - QFileInfo appExecutable(QDir(path), QFile::decodeName(s_appName)); - QByteArray cmd = QFile::encodeName(appExecutable.absoluteFilePath()); - s_autoRestartCommand = qstrdup(cmd.constData()); - } - - QStringList args = KCmdLineArgs::allArguments(); - args[0] = s_autoRestartCommand; // replace argv[0] with full path above - if (!args.contains("--nocrashhandler")) - args.insert(1, "--nocrashhandler"); - delete[] s_autoRestartCommandLine; - s_autoRestartArgc = args.count(); - s_autoRestartCommandLine = new char* [args.count() + 1]; - for (int i = 0; i < args.count(); ++i) { - s_autoRestartCommandLine[i] = qstrdup(QFile::encodeName(args.at(i)).constData()); - } - s_autoRestartCommandLine[args.count()] = 0; -} - -void -KCrash::setApplicationName(const QString& name) -{ - s_appName = qstrdup(QFile::encodeName(name).constData()); - - //update the autoRestartCommand - delete[] s_autoRestartCommand; - if (s_appPath) { - //if we have appPath, make autoRestartCommand be in the form "absolutePath/appName"... - QFileInfo appExecutable(QDir(QFile::decodeName(s_appPath)), name); - QByteArray cmd = QFile::encodeName(appExecutable.absoluteFilePath()); - s_autoRestartCommand = qstrdup(cmd.constData()); - } else { - //...else just use the appName for the autoRestartCommand - s_autoRestartCommand = qstrdup(s_appName); - } -} - -void KCrash::setDrKonqiEnabled(bool enabled) -{ - s_launchDrKonqi = enabled; - if (s_launchDrKonqi && !s_drkonqiPath) { - s_drkonqiPath = qstrdup(QFile::encodeName(KStandardDirs::findExe("drkonqi")).constData()); - if (!s_drkonqiPath) { - kError() << "Could not find drkonqi"; - s_launchDrKonqi = false; - } - } - - //we need at least the default crash handler to launch drkonqi - if (s_launchDrKonqi && !s_crashHandler) { - setCrashHandler(defaultCrashHandler); - } -} - -bool KCrash::isDrKonqiEnabled() -{ - return s_launchDrKonqi; -} - -void -KCrash::setCrashHandler (HandlerType handler) -{ - if (!handler) - handler = SIG_DFL; - - sigset_t mask; - sigemptyset(&mask); +static const int s_signals[] = { #ifdef SIGSEGV - signal (SIGSEGV, handler); - sigaddset(&mask, SIGSEGV); + SIGSEGV, #endif #ifdef SIGBUS - signal (SIGBUS, handler); - sigaddset(&mask, SIGBUS); + SIGBUS, #endif #ifdef SIGFPE - signal (SIGFPE, handler); - sigaddset(&mask, SIGFPE); + SIGFPE, #endif #ifdef SIGILL - signal (SIGILL, handler); - sigaddset(&mask, SIGILL); + SIGILL, #endif #ifdef SIGABRT - signal (SIGABRT, handler); - sigaddset(&mask, SIGABRT); + SIGABRT, #endif + 0 +}; - sigprocmask(SIG_UNBLOCK, &mask, 0); - - s_crashHandler = handler; +void KCrash::setFlags(KCrash::CrashFlags flags) +{ + s_flags = flags; + if (s_flags & KCrash::AutoRestart || s_flags & KCrash::DrKonqi) { + // Default crash handler is required for the flags to work but one may be set already + if (!s_crashHandler) { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); + if (args->isSet("crashhandler")) { + setCrashHandler(defaultCrashHandler); + } + } + } } -KCrash::HandlerType -KCrash::crashHandler() +KCrash::CrashFlags KCrash::flags() +{ + return s_flags; +} + +void KCrash::setCrashHandler(HandlerType handler) +{ + if (!handler) { + handler = SIG_DFL; + } + + s_crashHandler = handler; + + int counter = 0; + while (s_signals[counter]) { + KDE_signal(s_signals[counter], s_crashHandler); + counter++; + } +} + +KCrash::HandlerType KCrash::crashHandler() { return s_crashHandler; } -static void -closeAllFDs() +void KCrash::defaultCrashHandler(int sig) { - // Close all remaining file descriptors except for stdin/stdout/stderr - struct rlimit rlp; - getrlimit(RLIMIT_NOFILE, &rlp); - for (int i = 3; i < (int)rlp.rlim_cur; i++) - close(i); -} + KDE_signal(sig, SIG_DFL); -void -KCrash::defaultCrashHandler (int sig) -{ - // WABA: Do NOT use kDebug() in this function because it is much too risky! - // Handle possible recursions - static int crashRecursionCounter = 0; - crashRecursionCounter++; // Nothing before this, please ! - - signal(SIGALRM, SIG_DFL); - alarm(3); // Kill me... (in case we deadlock in malloc) - -#ifdef Q_OS_SOLARIS - (void) printstack(2 /* stderr, assuming it's still open. */); +#if 0 + kFatal() << QCoreApplication::applicationName() << "crashed" << "(" << QCoreApplication::applicationPid() << ")"; + return; #endif - if (crashRecursionCounter < 2) { - if (s_emergencySaveFunction) { - s_emergencySaveFunction (sig); - } - if ((s_flags & AutoRestart) && s_autoRestartCommand) { - sleep(1); - startProcess(s_autoRestartArgc, const_cast(s_autoRestartCommandLine), false); - } - crashRecursionCounter++; - } + if (s_flags & KCrash::AutoRestart) { + QStringList args; - // Note: KCrash closes FDs unconditionally later on if it forks to Dr Konqi - // and this program's FDs do not matter if kdeinit starts Dr Konqi. - if (!(s_flags & KeepFDs)) - closeAllFDs(); -# if defined(Q_WS_X11) - else if (QX11Info::display()) - close(ConnectionNumber(QX11Info::display())); -# endif - - if (crashRecursionCounter < 3) - { -#ifndef NDEBUG - fprintf(stderr, "KCrash: crashing... crashRecursionCounter = %d\n", - crashRecursionCounter); - fprintf(stderr, "KCrash: Application Name = %s path = %s pid = %lld\n", - s_appName ? s_appName : "" , - s_appPath ? s_appPath : "", QCoreApplication::applicationPid()); - fprintf(stderr, "KCrash: Arguments: "); - for (int i = 0; s_autoRestartCommandLine[i]; ++i) { - fprintf(stderr, "%s ", s_autoRestartCommandLine[i]); + // start up on the correct display + args.append(QString::fromLatin1("-display")); + if (QX11Info::display()) { + args.append(XDisplayString(QX11Info::display())); + } else { + args.append(QString::fromLocal8Bit(::getenv("DISPLAY"))); } - fprintf(stderr, "\n"); -#else - fprintf(stderr, "KCrash: Application '%s' crashing...\n", - s_appName ? s_appName : ""); -#endif - if (!s_launchDrKonqi) { - setCrashHandler(0); - raise(sig); // dump core, or whatever is the default action for this signal. + QProcess::startDetached(QCoreApplication::applicationFilePath(), args); + } else if (s_flags & KCrash::DrKonqi) { + const QString drkonqiexe = KStandardDirs::findExe(QString::fromLatin1("drkonqi")); + if (drkonqiexe.isEmpty()) { return; } - const char * argv[27]; // don't forget to update this - int i = 0; + QStringList args; - // argument 0 has to be drkonqi - argv[i++] = s_drkonqiPath; + args.append(QString::fromLatin1("--signal")); + args.append(QString::number(sig)); + args.append(QString::fromLatin1("--appname")); + args.append(QCoreApplication::applicationName()); + args.append(QString::fromLatin1("--apppath")); + args.append(QCoreApplication::applicationFilePath()); + args.append(QString::fromLatin1("--pid")); + args.append(QString::number(QCoreApplication::applicationPid())); -#if defined Q_WS_X11 - // start up on the correct display - argv[i++] = "-display"; - if ( QX11Info::display() ) - argv[i++] = XDisplayString(QX11Info::display()); - else - argv[i++] = getenv("DISPLAY"); -#elif defined(Q_WS_QWS) - // start up on the correct display - argv[i++] = "-display"; - argv[i++] = getenv("QWS_DISPLAY"); -#endif - - argv[i++] = "--appname"; - argv[i++] = s_appName ? s_appName : ""; - - if (KApplication::loadedByKdeinit) - argv[i++] = "--kdeinit"; - - // only add apppath if it's not NULL - if (s_appPath && *s_appPath) { - argv[i++] = "--apppath"; - argv[i++] = s_appPath; - } - - // signal number -- will never be NULL - char sigtxt[ 10 ]; - sprintf( sigtxt, "%d", sig ); - argv[i++] = "--signal"; - argv[i++] = sigtxt; - - char pidtxt[ 20 ]; - sprintf( pidtxt, "%lld", QCoreApplication::applicationPid()); - argv[i++] = "--pid"; - argv[i++] = pidtxt; - - const KComponentData componentData = KGlobal::mainComponent(); - const KAboutData *about = componentData.isValid() ? componentData.aboutData() : 0; - if (about) { - if (about->internalVersion()) { - argv[i++] = "--appversion"; - argv[i++] = about->internalVersion(); + const KComponentData kcomponentdata = KGlobal::mainComponent(); + const KAboutData *kaboutdata = kcomponentdata.isValid() ? kcomponentdata.aboutData() : nullptr; + if (kaboutdata) { + if (kaboutdata->internalVersion()) { + args.append(QString::fromLatin1("--appversion")); + args.append(kaboutdata->internalVersion()); } - if (about->internalProgramName()) { - argv[i++] = "--programname"; - argv[i++] = about->internalProgramName(); + if (kaboutdata->internalProgramName()) { + args.append(QString::fromLatin1("--programname")); + args.append(kaboutdata->internalProgramName()); } - if (about->internalBugAddress()) { - argv[i++] = "--bugaddress"; - argv[i++] = about->internalBugAddress(); + if (kaboutdata->internalBugAddress()) { + args.append(QString::fromLatin1("--bugaddress")); + args.append(kaboutdata->internalBugAddress()); } } - char sidtxt[256]; - if ( kapp && !kapp->startupId().isNull()) { - argv[i++] = "--startupid"; - strncpy(sidtxt, kapp->startupId().constData(), sizeof(sidtxt)); - argv[i++] = sidtxt; - } - - if ( s_flags & SaferDialog ) - argv[i++] = "--safer"; - - if ((s_flags & AutoRestart) && s_autoRestartCommand) - argv[i++] = "--restarted"; //tell drkonqi if the app has been restarted - - - // NULL terminated list - argv[i] = NULL; - - startProcess(i, argv, true); + QProcess::execute(drkonqiexe, args); } - if (crashRecursionCounter < 4) - { - fprintf(stderr, "Unable to start Dr. Konqi\n"); - } - - _exit(255); + ::exit(sig); } - - -static bool startProcessInternal(int argc, const char *argv[], bool waitAndExit, bool directly); -static pid_t startFromKdeinit(int argc, const char *argv[]); -static pid_t startDirectly(const char *argv[]); -static int write_socket(int sock, char *buffer, int len); -static int read_socket(int sock, char *buffer, int len); -static int openSocket(); - -void KCrash::startProcess(int argc, const char *argv[], bool waitAndExit) -{ - bool startDirectly = true; - - // First try to start the app via kdeinit, if the AlwaysDirectly flag hasn't been specified. - // This is done because it is dangerous to use fork() in the crash handler - // (there can be functions registered to be performed before fork(), for example handling - // of malloc locking, which doesn't work when malloc crashes because of heap corruption). - // Fails on Apple OSX+KDE4, because kdeinit4 is using the wrong socket name. - if (!(s_flags & AlwaysDirectly)) { - startDirectly = !startProcessInternal(argc, argv, waitAndExit, false); - } - - // If we can't reach kdeinit, we can still at least try to fork() - if (startDirectly) { - startProcessInternal(argc, argv, waitAndExit, true); - } -} - -static bool startProcessInternal(int argc, const char *argv[], bool waitAndExit, bool directly) -{ - fprintf(stderr, "KCrash: Attempting to start %s %s\n", argv[0], directly ? "directly" : "from kdeinit"); - - pid_t pid = directly ? startDirectly(argv) : startFromKdeinit(argc, argv); - - if (pid > 0 && waitAndExit) { - // Seems we made it.... - alarm(0); //stop the pending alarm that was set at the top of the defaultCrashHandler - - // Wait forever until the started process exits. This code path is executed - // when launching drkonqi. Note that drkonqi will stop this process in the meantime. - if (directly) { - //if the process was started directly, use waitpid(), as it's a child... - while(waitpid(-1, NULL, 0) != pid) {} - } else { - // PR_SET_PTRACER available since 3.4 -#if defined(Q_OS_LINUX) - // Declare the process that will be debugging the crashed KDE app (#245529) - prctl(PR_SET_PTRACER, pid, 0, 0, 0); -#endif - //...else poll its status using kill() - while(kill(pid, 0) >= 0) { - sleep(1); - } - } - _exit(253); - } - - return (pid > 0); //return true on success -} - -static pid_t startFromKdeinit(int argc, const char *argv[]) -{ - int socket = openSocket(); - if( socket < -1 ) - return 0; - klauncher_header header; - header.cmd = LAUNCHER_EXEC; - const int BUFSIZE = 8192; // make sure this is big enough - char buffer[ BUFSIZE + 10 ]; - int pos = 0; - long argcl = argc; - memcpy( buffer + pos, &argcl, sizeof( argcl )); - pos += sizeof( argcl ); - for( int i = 0; - i < argc; - ++i ) - { - int len = strlen( argv[ i ] ) + 1; // include terminating \0 - if( pos + len >= BUFSIZE ) - { - fprintf( stderr, "BUFSIZE in KCrash not big enough!\n" ); - return 0; - } - memcpy( buffer + pos, argv[ i ], len ); - pos += len; - } - long env = 0; - memcpy( buffer + pos, &env, sizeof( env )); - pos += sizeof( env ); - long avoid_loops = 0; - memcpy( buffer + pos, &avoid_loops, sizeof( avoid_loops )); - pos += sizeof( avoid_loops ); - header.arg_length = pos; - write_socket(socket, (char *) &header, sizeof(header)); - write_socket(socket, buffer, pos); - if( read_socket( socket, (char *) &header, sizeof(header)) < 0 - || header.cmd != LAUNCHER_OK ) - { - return 0; - } - long pid; - read_socket(socket, (char *) &pid, sizeof(pid)); - return static_cast(pid); -} - -static pid_t startDirectly(const char *argv[]) -{ - pid_t pid = fork(); - switch (pid) - { - case -1: - fprintf( stderr, "KCrash failed to fork(), errno = %d\n", errno ); - return 0; - case 0: - if (setgid(getgid()) < 0 || setuid(getuid()) < 0) - _exit(253); // This cannot happen. Theoretically. - closeAllFDs(); // We are in the child now. Close FDs unconditionally. - execvp(argv[0], const_cast< char** >(argv)); - fprintf( stderr, "KCrash failed to exec(), errno = %d\n", errno ); - _exit(253); - default: - return pid; - } -} - -// From now on this code is copy&pasted from kinit/wrapper.c : - -static char *getDisplay() -{ - const char *display; - char *result; - char *screen; - char *colon; - char *i; - -/* - don't test for a value from qglobal.h but instead distinguish - Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS - on the commandline (which in qglobal.h however triggers Q_WS_QWS, - but we don't want to include that here) (Simon) -#ifdef Q_WS_X11 - */ -#if defined(NO_DISPLAY) - display = "NODISPLAY"; -#elif !defined(QWS) - display = getenv("DISPLAY"); -#else - display = getenv("QWS_DISPLAY"); -#endif - if (!display || !*display) - { - display = ":0"; - } - result = (char*)malloc(strlen(display)+1); - if (result == NULL) - return NULL; - - strcpy(result, display); - screen = strrchr(result, '.'); - colon = strrchr(result, ':'); - if (screen && (screen > colon)) - *screen = '\0'; - while((i = strchr(result, ':'))) - *i = '_'; - return result; -} - -/* - * Write 'len' bytes from 'buffer' into 'sock'. - * returns 0 on success, -1 on failure. - */ -static int write_socket(int sock, char *buffer, int len) -{ - ssize_t result; - int bytes_left = len; - while ( bytes_left > 0) - { - result = write(sock, buffer, bytes_left); - if (result > 0) - { - buffer += result; - bytes_left -= result; - } - else if (result == 0) - return -1; - else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) - return -1; - } - return 0; -} - -/* - * Read 'len' bytes from 'sock' into 'buffer'. - * returns 0 on success, -1 on failure. - */ -static int read_socket(int sock, char *buffer, int len) -{ - ssize_t result; - int bytes_left = len; - while ( bytes_left > 0) - { - result = read(sock, buffer, bytes_left); - if (result > 0) - { - buffer += result; - bytes_left -= result; - } - else if (result == 0) - return -1; - else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) - return -1; - } - return 0; -} - -static int openSocket() -{ - socklen_t socklen; - int s; - struct sockaddr_un server; -#define MAX_SOCK_FILE 255 - char sock_file[MAX_SOCK_FILE + 1]; - const QByteArray tmp_dir = QFile::encodeName(KGlobal::dirs()->resourceDirs("tmp").first()); - char *display; - - sock_file[0] = sock_file[MAX_SOCK_FILE] = 0; - - if (tmp_dir.isEmpty()) - { - fprintf(stderr, "Warning: no temp dir!\n"); - return -1; - } - strncat(sock_file, tmp_dir.constData(), MAX_SOCK_FILE - strlen(sock_file)); - - /** Strip trailing '/' **/ - if ( sock_file[strlen(sock_file)-1] == '/') - sock_file[strlen(sock_file)-1] = 0; - - sock_file[sizeof(sock_file)-1] = '\0'; - - /* append $DISPLAY */ - display = getDisplay(); -#if !defined (NO_DISPLAY) - if (display == NULL) - { - fprintf(stderr, "Error: Could not determine display.\n"); - return -1; - } -#endif - - if (strlen(sock_file)+strlen(display)+strlen("/kdeinit4_")+2 > MAX_SOCK_FILE) - { - fprintf(stderr, "Warning: Socket name will be too long.\n"); - free (display); - return -1; - } - strcat(sock_file, "/kdeinit4_"); -#if !defined (NO_DISPLAY) - strcat(sock_file, display); - free(display); -#endif - - if (strlen(sock_file) >= sizeof(server.sun_path)) - { - fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n"); - return -1; - } - - /* - * create the socket stream - */ - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s < 0) - { - perror("Warning: socket() failed: "); - return -1; - } - - server.sun_family = AF_UNIX; - strcpy(server.sun_path, sock_file); - // Use stderr, to make the message visible on the Apple OS X Console log. - fprintf(stderr, "KCrash: Connect sock_file=%s\n", sock_file); - socklen = sizeof(server); - if(connect(s, (struct sockaddr *)&server, socklen) == -1) - { - perror("Warning: connect() failed: "); - close(s); - return -1; - } - return s; -} - diff --git a/kdeui/util/kcrash.h b/kdeui/util/kcrash.h index 7475d132..026cd78c 100644 --- a/kdeui/util/kcrash.h +++ b/kdeui/util/kcrash.h @@ -1,23 +1,20 @@ -/* - * This file is part of the KDE Libraries - * Copyright (C) 2000 Timo Hummel - * Tom Braun - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ #ifndef KCRASH_H #define KCRASH_H @@ -45,110 +42,66 @@ */ namespace KCrash { - /** - * The default crash handler. - * Do not call this function directly. Instead, use - * setCrashHandler() to set it as your application's crash handler. - * @param signal the signal number - * @note If you implement your own crash handler, you will have to - * call this function from your implementation if you want to use the - * features of this namespace. - */ - KDEUI_EXPORT void defaultCrashHandler (int signal); + /** + * The default crash handler. + * Do not call this function directly. Instead, use + * setCrashHandler() to set it as your application's crash handler. + * @param signal the signal number + * @note If you implement your own crash handler, you will have to + * call this function from your implementation if you want to use the + * features of this namespace. + */ + KDEUI_EXPORT void defaultCrashHandler (int signal); - /** - * Typedef for a pointer to a crash handler function. - * The function's argument is the number of the signal. - */ - typedef void (*HandlerType)(int); + /** + * Typedef for a pointer to a crash handler function. + * The function's argument is the number of the signal. + */ + typedef void (*HandlerType)(int); - /** - * Install a function to be called when a crash occurs. - * A crash occurs when one of the following signals is - * caught: SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT. - * @param handler this can be one of: - * @li null, in which case signal catching is disabled - * (by setting the signal handler for the crash signals to SIG_DFL) - * @li a user defined function in the form: - * static (if in a class) void myCrashHandler(int); - * @li if handler is omitted, the default crash handler is installed - * @note If you use setDrKonqiEnabled(true), setEmergencySaveFunction(myfunc) - * or setFlags(AutoRestart), you do not need to call this function - * explicitly. The default crash handler is automatically installed by - * those functions if needed. However, if you set a custom crash handler, - * those functions will not change it. - */ - KDEUI_EXPORT void setCrashHandler (HandlerType handler = defaultCrashHandler); + /** + * Install a function to be called when a crash occurs. + * A crash occurs when one of the following signals is + * caught: SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT. + * @param handler this can be one of: + * @li null, in which case signal catching is disabled + * (by setting the signal handler for the crash signals to SIG_DFL) + * @li a user defined function in the form: + * static (if in a class) void myCrashHandler(int); + * @li if handler is omitted, the default crash handler is installed + * @note If you use setFlags(AutoRestart), you do not need to call this function + * explicitly. The default crash handler is automatically installed by + * those functions if needed. However, if you set a custom crash handler, + * those functions will not change it. + */ + KDEUI_EXPORT void setCrashHandler(HandlerType handler = defaultCrashHandler); - /** - * Returns the installed crash handler. - * @return the crash handler - */ - KDEUI_EXPORT HandlerType crashHandler(); + /** + * Returns the installed crash handler. + * @return the crash handler + */ + KDEUI_EXPORT HandlerType crashHandler(); - /** - * Installs a function which should try to save the application's data. - * @note It is the crash handler's responsibility to call this function. - * Therefore, if no crash handler is set, the default crash handler - * is installed to ensure the save function will be called. - * @param saveFunction the handler to install - */ - KDEUI_EXPORT void setEmergencySaveFunction (HandlerType saveFunction = 0); + /** + * Options to determine how the default crash handler should behave. + */ + enum CrashFlag { + AutoRestart = 1, ///< autorestart this application. Only sensible for KUniqueApplications. @since 4.1. + DrKonqi = 2 ///< launchers DrKonqi. @since 4.23. + }; + Q_DECLARE_FLAGS(CrashFlags, CrashFlag) - /** - * Returns the currently set emergency save function. - * @return the emergency save function - */ - KDEUI_EXPORT HandlerType emergencySaveFunction(); + /** + * Set options to determine how the default crash handler should behave. + * @param flags ORed together CrashFlags + */ + KDEUI_EXPORT void setFlags(CrashFlags flags); - /** - * Options to determine how the default crash handler should behave. - */ - enum CrashFlag { - KeepFDs = 1, ///< don't close all file descriptors immediately - SaferDialog = 2, ///< start DrKonqi without arbitrary disk access - AlwaysDirectly = 4, ///< never try to to start DrKonqi via kdeinit. Use fork() and exec() instead. - AutoRestart = 8 ///< autorestart this application. Only sensible for KUniqueApplications. @since 4.1. - }; - Q_DECLARE_FLAGS(CrashFlags, CrashFlag) - - /** - * Set options to determine how the default crash handler should behave. - * @param flags ORed together CrashFlags - */ - KDEUI_EXPORT void setFlags( CrashFlags flags ); - - /** - * Sets the application @p path which should be passed to - * DrKonqi, our nice crash display application. - * @param path the application path - */ - KDEUI_EXPORT void setApplicationPath (const QString &path); - - /** - * Sets the application @p name which should be passed to - * DrKonqi, our nice crash display application. - * @param name the name of the application, as shown in DrKonqi - */ - KDEUI_EXPORT void setApplicationName (const QString &name); - - /** - * Enables or disables launching DrKonqi from the crash handler. - * By default, launching DrKonqi is disabled. However, KApplication - * will enable it in its constructor, so you don't need to call this - * function if you are using KApplication. - * @note It is the crash handler's responsibility to launch DrKonqi. - * Therefore, if no crash handler is set, this method also installs - * the default crash handler to ensure that DrKonqi will be launched. - * @since 4.5 - */ - KDEUI_EXPORT void setDrKonqiEnabled(bool enabled); - - /** - * Returns true if DrKonqi is set to be launched from the crash handler or false otherwise. - * @since 4.5 - */ - KDEUI_EXPORT bool isDrKonqiEnabled(); + /** + * Get currently set options that determine how the default crash handler should behave. + * @param flags ORed together CrashFlags + */ + KDEUI_EXPORT CrashFlags flags(); } Q_DECLARE_OPERATORS_FOR_FLAGS(KCrash::CrashFlags) diff --git a/kinit/CMakeLists.txt b/kinit/CMakeLists.txt index 9913d7b5..6d637d47 100644 --- a/kinit/CMakeLists.txt +++ b/kinit/CMakeLists.txt @@ -1,45 +1,10 @@ -project(kdeinit) +project(kinit) if(ENABLE_TESTING) add_subdirectory(tests) endif() -include_directories(${KDE4_KPARTS_INCLUDES}) - -########### Configuring (mainly for proctitle) ########### - -include(CheckFunctionExists) -include(CheckVariableExists) - -check_variable_exists(__progname HAVE___PROGNAME) -check_variable_exists(__progname_full HAVE___PROGNAME_FULL) -check_function_exists(setproctitle HAVE_SETPROCTITLE) - -set(KINIT_SOCKET_LIBRARY) -if(HAVE_SOCKET_LIBRARY) - set(KINIT_SOCKET_LIBRARY socket) -endif() - -if (NOT Q_WS_X11 AND NOT Q_WS_QWS) - add_definitions(-DNO_DISPLAY) -endif() - -########### kdeinit4 ############### - -set(kdeinit_SRCS - kinit.cpp - proctitle.cpp - klauncher_cmds.cpp -) - -add_executable(kdeinit4 ${kdeinit_SRCS}) - -target_link_libraries(kdeinit4 ${KDE4_KDEUI_LIBS} ${KINIT_SOCKET_LIBRARY}) -if (Q_WS_X11) - target_link_libraries(kdeinit4 ${X11_X11_LIB}) -endif() - -install(TARGETS kdeinit4 ${INSTALL_TARGETS_DEFAULT_ARGS}) +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1210) ########### kioslave ############### @@ -49,14 +14,11 @@ target_link_libraries(kioslave ${QT_QTCORE_LIBRARY}) install(TARGETS kioslave DESTINATION ${KDE4_LIBEXEC_INSTALL_DIR}) - ########### klauncher ############### + set(klauncher_SRCS klauncher.cpp - klauncher_main.cpp klauncher_adaptor.cpp - autostart.cpp - klauncher_cmds.cpp # just so that it gets generated ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KLauncher.xml ) @@ -64,11 +26,16 @@ set(klauncher_SRCS # The adaptor is manually edited, generate the xml from it. qt4_generate_dbus_interface(klauncher_adaptor.h org.kde.KLauncher.xml) -add_executable(klauncher ${klauncher_SRCS}) +add_executable(klauncher4 ${klauncher_SRCS}) -target_link_libraries(klauncher ${KDE4_KIO_LIBS} ${X11_LIBRARIES}) +target_link_libraries(klauncher4 + ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} +) -install(TARGETS klauncher DESTINATION ${KDE4_LIBEXEC_INSTALL_DIR}) +kde4_add_dbus_service(org.kde.klauncher.service.in) + +install(TARGETS klauncher4 DESTINATION ${KDE4_BIN_INSTALL_DIR}) ########### install files ############### @@ -77,10 +44,3 @@ install( ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KLauncher.xml DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR} ) - -########### config-kdeinit.h ############ - -configure_file( - config-kdeinit.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/config-kdeinit.h -) diff --git a/kinit/autostart.cpp b/kinit/autostart.cpp deleted file mode 100644 index b9d05244..00000000 --- a/kinit/autostart.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of the KDE libraries - * Copyright (c) 2001 Waldo Bastian - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License version 2 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - **/ - -#define QT_NO_CAST_FROM_ASCII -#include "autostart.h" - -#include -#include -#include - -class AutoStartItem -{ -public: - QString name; - QString service; - int phase; -}; - -AutoStart::AutoStart() - : m_phase(-1), m_phasedone(false) -{ - m_startList = new AutoStartList; - KGlobal::dirs()->addResourceType("xdgconf-autostart", NULL, QLatin1String("autostart/")); // xdg ones - KGlobal::dirs()->addResourceType("autostart", "xdgconf-autostart", QLatin1String("/")); // merge them - KGlobal::dirs()->addResourceType("autostart", 0, QLatin1String("share/autostart")); // KDE ones are higher priority -} - -AutoStart::~AutoStart() -{ - qDeleteAll(*m_startList); - m_startList->clear(); - delete m_startList; -} - -void -AutoStart::setPhase(int phase) -{ - if (phase > m_phase) - { - m_phase = phase; - m_phasedone = false; - } -} - -void AutoStart::setPhaseDone() -{ - m_phasedone = true; -} - -static QString extractName(QString path) // krazy:exclude=passbyvalue -{ - int i = path.lastIndexOf(QLatin1Char('/')); - if (i >= 0) - path = path.mid(i+1); - i = path.lastIndexOf(QLatin1Char('.')); - if (i >= 0) - path = path.left(i); - return path; -} - -void -AutoStart::loadAutoStartList() -{ - const QStringList files = KGlobal::dirs()->findAllResources("autostart", - QString::fromLatin1("*.desktop"), - KStandardDirs::NoDuplicates); - - foreach(const QString it, files) - { - KAutostart config(it); - if( !config.autostarts(QString::fromLatin1("KDE"), KAutostart::CheckAll)) - continue; - - AutoStartItem *item = new AutoStartItem; - item->name = extractName(it); - item->service = it; - item->phase = config.startPhase(); - if (item->phase < 0) - item->phase = 0; - m_startList->append(item); - } -} - -QString -AutoStart::startService() -{ - if (m_startList->isEmpty()) - return QString(); - - // Just start something in this phase - QMutableListIterator it(*m_startList); - while (it.hasNext()) - { - AutoStartItem *item = it.next(); - if (item->phase == m_phase) - { - m_started.prepend(item->name); - QString service = item->service; - it.remove(); - delete item; - return service; - } - } - - return QString(); -} diff --git a/kinit/autostart.h b/kinit/autostart.h deleted file mode 100644 index 0dad82d6..00000000 --- a/kinit/autostart.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 2001 Waldo Bastian - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef _AUTOSTART_H_ -#define _AUTOSTART_H_ - -#include - -class AutoStartItem; -typedef QList AutoStartList; - -class AutoStart -{ -public: - AutoStart(); - ~AutoStart(); - - void loadAutoStartList(); - QString startService(); - void setPhase(int phase); - void setPhaseDone(); - int phase() const { return m_phase; } - bool phaseDone() const { return m_phasedone; } - -private: - AutoStartList *m_startList; - QStringList m_started; - int m_phase; - bool m_phasedone; -}; - -#endif diff --git a/kinit/config-kdeinit.h.cmake b/kinit/config-kdeinit.h.cmake deleted file mode 100644 index 5a62c203..00000000 --- a/kinit/config-kdeinit.h.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -/* These are for proctitle.cpp: */ -#cmakedefine HAVE___PROGNAME 1 -#cmakedefine HAVE___PROGNAME_FULL 1 -#cmakedefine HAVE_SETPROCTITLE 1 diff --git a/kinit/kinit.cpp b/kinit/kinit.cpp deleted file mode 100644 index 19a35be1..00000000 --- a/kinit/kinit.cpp +++ /dev/null @@ -1,1533 +0,0 @@ -/* - * This file is part of the KDE libraries - * Copyright (c) 1999-2000 Waldo Bastian - * (c) 1999 Mario Weilguni - * (c) 2001 Lubos Lunak - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License version 2 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#define QT_NO_CAST_FROM_ASCII - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include // Needed on some systems. -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "klauncher_cmds.h" -#include "proctitle.h" - -#ifdef Q_OS_LINUX -#include -#ifndef PR_SET_NAME -#define PR_SET_NAME 15 -#endif -#endif - -#ifdef Q_WS_X11 -#include -#include -#include -#include -#endif - -// #define SKIP_PROCTITLE 1 - -extern char **environ; - -#ifdef Q_WS_X11 -static int X11fd = -1; -static Display *X11display = 0; -static int X11_startup_notify_fd = -1; -static Display *X11_startup_notify_display = 0; -#endif -static KComponentData *s_instance = 0; -#define MAX_SOCK_FILE 255 -static char sock_file[MAX_SOCK_FILE]; - -#ifdef Q_WS_X11 -#define DISPLAY "DISPLAY" -#elif defined(Q_WS_QWS) -#define DISPLAY "QWS_DISPLAY" -#else -#error Use QT/X11 or QT/Embedded -#endif - -/* Group data */ -static struct { - int maxname; - int fd[2]; - int launcher[2]; /* socket pair for launcher communication */ - int deadpipe[2]; /* pipe used to detect dead children */ - int initpipe[2]; - int wrapper; /* socket for wrapper communication */ - int accepted_fd; /* socket accepted and that must be closed in the child process */ - char result; - int exit_status; - pid_t fork; - pid_t launcher_pid; - int n; - char **argv; - int (*func)(int, char *[]); - int (*launcher_func)(int); - bool debug_wait; - QByteArray errorMsg; - bool launcher_ok; - bool suicide; -} d; - -struct child -{ - pid_t pid; - int sock; /* fd to write message when child is dead*/ - struct child *next; -}; - -static struct child *children; - -#ifdef Q_WS_X11 -extern "C" { -int kdeinit_xio_errhandler( Display * ); -int kdeinit_x_errhandler( Display *, XErrorEvent *err ); -} -#endif - -/* - * Clean up the file descriptor table by closing all file descriptors - * that are still open. - * - * This function is called very early in the main() function, so that - * we don't leak anything that was leaked to us. - */ -static void cleanup_fds() -{ - int maxfd = FD_SETSIZE; - struct rlimit rl; - if (getrlimit(RLIMIT_NOFILE, &rl) == 0) - maxfd = rl.rlim_max; - for (int fd = 3; fd < maxfd; ++fd) - { - close(fd); - } -} - -/* - * Close fd's which are only useful for the parent process. - * Restore default signal handlers. - */ -static void close_fds() -{ - while (struct child *child = children) { - close(child->sock); - children = child->next; - free(child); - } - - if (d.deadpipe[0] != -1) - { - close(d.deadpipe[0]); - d.deadpipe[0] = -1; - } - - if (d.deadpipe[1] != -1) - { - close(d.deadpipe[1]); - d.deadpipe[1] = -1; - } - - if (d.initpipe[0] != -1) - { - close(d.initpipe[0]); - d.initpipe[0] = -1; - } - - if (d.initpipe[1] != -1) - { - close(d.initpipe[1]); - d.initpipe[1] = -1; - } - - if (d.launcher[0] != -1) - { - close(d.launcher[0]); - d.launcher[0] = -1; - } - if (d.wrapper != -1) - { - close(d.wrapper); - d.wrapper = -1; - } - if (d.accepted_fd != -1) - { - close(d.accepted_fd); - d.accepted_fd = -1; - } -#ifdef Q_WS_X11 - if (X11fd >= 0) - { - close(X11fd); - X11fd = -1; - } - if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd ) - { - close(X11_startup_notify_fd); - X11_startup_notify_fd = -1; - } -#endif - - KDE_signal(SIGCHLD, SIG_DFL); - KDE_signal(SIGPIPE, SIG_DFL); -} - -/* Notify wrapper program that the child it started has finished. */ -static void child_died(pid_t exit_pid, int exit_status) -{ - struct child *child, **childptr = &children; - - while ((child = *childptr)) - { - if (child->pid == exit_pid) - { - /* Send a message with the return value of the child on the control socket */ - klauncher_header request_header; - long request_data[2]; - request_header.cmd = LAUNCHER_CHILD_DIED; - request_header.arg_length = sizeof(long) * 2; - request_data[0] = exit_pid; - request_data[1] = exit_status; - write(child->sock, &request_header, sizeof(request_header)); - write(child->sock, request_data, request_header.arg_length); - close(child->sock); - - *childptr = child->next; - free(child); - return; - } - - childptr = &child->next; - } -} - - -static void exitWithErrorMsg(const QString &errorMsg) -{ - fprintf( stderr, "%s\n", errorMsg.toLocal8Bit().data() ); - QByteArray utf8ErrorMsg = errorMsg.toUtf8(); - d.result = 3; // Error with msg - write(d.fd[1], &d.result, 1); - int l = utf8ErrorMsg.length(); - write(d.fd[1], &l, sizeof(int)); - write(d.fd[1], utf8ErrorMsg.data(), l); - close(d.fd[1]); - exit(255); -} - -static void setup_tty( const char* tty ) -{ - if( tty == NULL || *tty == '\0' ) - return; - int fd = KDE_open( tty, O_WRONLY ); - if( fd < 0 ) - { - perror( "kdeinit4: could not open() tty" ); - return; - } - if( dup2( fd, STDOUT_FILENO ) < 0 ) - { - perror( "kdeinit4: could not dup2() stdout tty" ); - } - if( dup2( fd, STDERR_FILENO ) < 0 ) - { - perror( "kdeinit4: could not dup2() stderr tty" ); - } - close( fd ); -} - -// from kdecore/netwm.cpp -static int get_current_desktop( Display* disp ) -{ - int desktop = 0; // no desktop by default -#ifdef Q_WS_X11 // Only X11 supports multiple desktops - Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False ); - Atom type_ret; - int format_ret; - unsigned char *data_ret; - unsigned long nitems_ret, unused; - if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop, - 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret ) - == Success) - { - if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) - desktop = *((long *) data_ret) + 1; - if (data_ret) - XFree ((char*) data_ret); - } -#endif - return desktop; -} - -// var has to be e.g. "DISPLAY=", i.e. with = -const char* get_env_var( const char* var, int envc, const char* envs ) -{ - if( envc > 0 ) - { // get the var from envs - const char* env_l = envs; - int ln = strlen( var ); - for (int i = 0; i < envc; i++) - { - if( strncmp( env_l, var, ln ) == 0 ) - return env_l + ln; - while(*env_l != 0) env_l++; - env_l++; - } - } - return NULL; -} - -#ifdef Q_WS_X11 -static void init_startup_info( KStartupInfoId& id, const char* bin, - int envc, const char* envs ) -{ - const char* dpy = get_env_var( DISPLAY"=", envc, envs ); - // this may be called in a child, so it can't use display open using X11display - // also needed for multihead - X11_startup_notify_display = XOpenDisplay( dpy ); - if( X11_startup_notify_display == NULL ) - return; - X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display ); - KStartupInfoData data; - int desktop = get_current_desktop( X11_startup_notify_display ); - data.setDesktop( desktop ); - data.setBin(QFile::decodeName(bin)); - KStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); - XFlush( X11_startup_notify_display ); -} - -static void complete_startup_info( KStartupInfoId& id, pid_t pid ) -{ - if( X11_startup_notify_display == NULL ) - return; - if( pid == 0 ) // failure - KStartupInfo::sendFinishX( X11_startup_notify_display, id ); - else - { - KStartupInfoData data; - data.addPid( pid ); - data.setHostname(); - KStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); - } - XCloseDisplay( X11_startup_notify_display ); - X11_startup_notify_display = NULL; - X11_startup_notify_fd = -1; -} -#endif - -static QByteArray execpath_avoid_loops( const QByteArray& exec, int envc, const char* envs, bool avoid_loops ) -{ - QStringList paths; - static const QChar pathSep = QChar::fromLatin1(':'); - if( envc > 0 ) /* use the passed environment */ - { - const char* path = get_env_var( "PATH=", envc, envs ); - if( path != NULL ) - paths = QFile::decodeName(path).split(pathSep); - } else { - paths = QString::fromLocal8Bit(qgetenv("PATH")).split(pathSep, QString::KeepEmptyParts); - } - QString execpath = s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(pathSep)); - if (avoid_loops && !execpath.isEmpty()) { - const int pos = execpath.lastIndexOf(QLatin1Char('/')); - const QString bin_path = execpath.left(pos); - for( QStringList::Iterator it = paths.begin(); - it != paths.end(); - ++it ) { - if( *it == bin_path || *it == bin_path + QLatin1Char('/')) { - paths.erase( it ); - break; // --> - } - } - execpath = s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(pathSep)); - } - return QFile::encodeName(execpath); -} - -static pid_t launch(int argc, const char *_name, const char *args, - const char *cwd=0, int envc=0, const char *envs=0, - const char *tty=0, bool avoid_loops = false, - const char* startup_id_str = "0" ) // krazy:exclude=doublequote_chars -{ - QString lib; - QByteArray name; - QByteArray exec; - QString libpath; - QByteArray execpath; - - if (_name[0] != '/') { - name = _name; - lib = QFile::decodeName(name); - exec = name; - KPluginLoader klib(lib, *s_instance ); - libpath = klib.fileName(); - execpath = execpath_avoid_loops(exec, envc, envs, avoid_loops); - } else { - name = _name; - lib = QFile::decodeName(name); - name = name.mid(name.lastIndexOf('/') + 1); - exec = _name; - if (lib.endsWith(QLatin1String(".so"))) - libpath = lib; - else { - execpath = exec; - } - } -#ifndef NDEBUG - fprintf(stderr,"kdeinit4: preparing to launch %s\n", libpath.isEmpty() - ? execpath.constData() : libpath.toUtf8().constData()); -#endif - if (!args) { - argc = 1; - } - - if (0 > pipe(d.fd)) - { - perror("kdeinit4: pipe() failed"); - d.result = 3; - d.errorMsg = i18n("Unable to start new process.\n" - "The system may have reached the maximum number of open files possible or the maximum number of open files that you are allowed to use has been reached.").toUtf8(); - d.fork = 0; - return d.fork; - } - -#ifdef Q_WS_X11 - KStartupInfoId startup_id; - startup_id.initId( startup_id_str ); - if( !startup_id.none()) - init_startup_info( startup_id, name, envc, envs ); -#endif - // find out this path before forking, doing it afterwards - // crashes on some platforms, notably OSX - - d.errorMsg = 0; - d.fork = fork(); - switch(d.fork) { - case -1: - perror("kdeinit4: fork() failed"); - d.result = 3; - d.errorMsg = i18n("Unable to create new process.\n" - "The system may have reached the maximum number of processes possible or the maximum number of processes that you are allowed to use has been reached.").toUtf8(); - close(d.fd[0]); - close(d.fd[1]); - d.fork = 0; - break; - case 0: - { - // Child - close(d.fd[0]); - close_fds(); - - // Try to chdir, either to the requested directory or to the user's document path by default. - // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist, - // we still want to execute `foo` even if the chdir() failed. - if (cwd && *cwd) { - (void)chdir(cwd); - } - - for (int i = 0; i < envc; i++) - { - putenv((char *)envs); - while(*envs != 0) envs++; - envs++; - } - -#ifdef Q_WS_X11 - if( startup_id.none()) - KStartupInfo::resetStartupEnv(); - else - startup_id.setupStartupEnv(); -#endif - { - int r; - QByteArray procTitle; - d.argv = (char **) malloc(sizeof(char *) * (argc+1)); - d.argv[0] = (char *) _name; - for (int i = 1; i < argc; i++) - { - d.argv[i] = (char *) args; - procTitle += ' '; - procTitle += (char *) args; - while(*args != 0) args++; - args++; - } - d.argv[argc] = 0; - -#ifndef SKIP_PROCTITLE - // Give the process a new name -#ifdef Q_OS_LINUX - // Set the process name so that killall works like intended - r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0); - if ( r == 0 ) - proctitle_set( "%s [kdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" ); - else - proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" ); -#else - proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" ); -#endif -#endif - } - - if (libpath.isEmpty() && execpath.isEmpty()) - { - QString errorMsg = i18n("Could not find '%1' executable.", QFile::decodeName(_name)); - exitWithErrorMsg(errorMsg); - } - - QLibrary l(libpath); - - if ( !libpath.isEmpty() ) - { - if (!l.load() || !l.isLoaded() ) - { - QString ltdlError (l.errorString()); - if (execpath.isEmpty()) - { - // Error - QString errorMsg = i18n("Could not open library '%1'.\n%2", libpath, ltdlError); - exitWithErrorMsg(errorMsg); - } - else - { - // Print warning - fprintf(stderr, "Could not open library %s: %s\n", qPrintable(lib), - qPrintable(ltdlError) ); - } - } - } - if (!l.isLoaded()) - { - d.result = 2; // Try execing - write(d.fd[1], &d.result, 1); - - // We set the close on exec flag. - // Closing of d.fd[1] indicates that the execvp succeeded! - fcntl(d.fd[1], F_SETFD, FD_CLOEXEC); - - setup_tty( tty ); - - if (!execpath.isEmpty()) - execvp(execpath.constData(), d.argv); - - d.result = 1; // Error - write(d.fd[1], &d.result, 1); - close(d.fd[1]); - exit(255); - } - - void * sym = l.resolve( "kdemain" ); - if ( !sym ) - { - QString ltdlError = l.errorString(); - fprintf(stderr, "Could not find kdemain: %s\n", qPrintable(ltdlError) ); - QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2", - libpath, ltdlError); - exitWithErrorMsg(errorMsg); - } - - d.result = 0; // Success - write(d.fd[1], &d.result, 1); - close(d.fd[1]); - - d.func = (int (*)(int, char *[])) sym; - if (d.debug_wait) - { - fprintf(stderr, "kdeinit4: Suspending process\n" - "kdeinit4: 'gdb kdeinit4 %d' to debug\n" - "kdeinit4: 'kill -SIGCONT %d' to continue\n", - getpid(), getpid()); - kill(getpid(), SIGSTOP); - } - else - { - setup_tty( tty ); - } - - exit( d.func(argc, d.argv)); /* Launch! */ - - break; - } - default: - // Parent - close(d.fd[1]); - bool exec = false; - for(;;) - { - d.n = read(d.fd[0], &d.result, 1); - if (d.n == 1) - { - if (d.result == 2) - { -#ifndef NDEBUG - //fprintf(stderr, "kdeinit4: no kdeinit module, trying exec....\n"); -#endif - exec = true; - continue; - } - if (d.result == 3) - { - int l = 0; - d.n = read(d.fd[0], &l, sizeof(int)); - if (d.n == sizeof(int)) - { - QByteArray tmp; - tmp.resize(l+1); - d.n = read(d.fd[0], tmp.data(), l); - tmp[l] = 0; - if (d.n == l) - d.errorMsg = tmp; - } - } - // Finished - break; - } - if (d.n == -1) - { - if (errno == ECHILD) { // a child died. - continue; - } - if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read - continue; - } - } - if (d.n == 0) - { - if (exec) { - d.result = 0; - } else { - fprintf(stderr,"kdeinit4: (%s %s) Pipe closed unexpectedly", name.constData(), execpath.constData()); - perror("kdeinit4: Pipe closed unexpectedly"); - d.result = 1; // Error - } - break; - } - perror("kdeinit4: Error reading from pipe"); - d.result = 1; // Error - break; - } - close(d.fd[0]); - } -#ifdef Q_WS_X11 - if( !startup_id.none()) - { - if( d.fork && d.result == 0 ) // launched successfully - complete_startup_info( startup_id, d.fork ); - else // failure, cancel ASN - complete_startup_info( startup_id, 0 ); - } -#endif - return d.fork; -} - -extern "C" { - -static void sig_child_handler(int) -{ - /* - * Write into the pipe of death. - * This way we are sure that we return from the select() - * - * A signal itself causes select to return as well, but - * this creates a race-condition in case the signal arrives - * just before we enter the select. - */ - char c = 0; - write(d.deadpipe[1], &c, 1); -} - -} - -static void init_signals() -{ - struct sigaction act; - long options; - - if (pipe(d.deadpipe) != 0) - { - perror("kdeinit4: Aborting. Can not create pipe"); - exit(255); - } - - options = fcntl(d.deadpipe[0], F_GETFL); - if (options == -1) - { - perror("kdeinit4: Aborting. Can not make pipe non-blocking"); - exit(255); - } - - if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1) - { - perror("kdeinit4: Aborting. Can not make pipe non-blocking"); - exit(255); - } - - /* - * A SIGCHLD handler is installed which sends a byte into the - * pipe of death. This is to ensure that a dying child causes - * an exit from select(). - */ - act.sa_handler=sig_child_handler; - sigemptyset(&(act.sa_mask)); - sigaddset(&(act.sa_mask), SIGCHLD); - sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); - act.sa_flags = SA_NOCLDSTOP; - - // CC: take care of SunOS which automatically restarts interrupted system - // calls (and thus does not have SA_RESTART) - -#ifdef SA_RESTART - act.sa_flags |= SA_RESTART; -#endif - sigaction( SIGCHLD, &act, 0L); - - act.sa_handler=SIG_IGN; - sigemptyset(&(act.sa_mask)); - sigaddset(&(act.sa_mask), SIGPIPE); - sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); - act.sa_flags = 0; - sigaction( SIGPIPE, &act, 0L); -} - -static void shutdown_kdeinit() -{ - QT_SOCKLEN_T socklen; - - /** Test if socket file is already present - * note that access() resolves symlinks, and so we check the actual - * socket file if it exists - */ - if (access(sock_file, W_OK) == 0) - { - int s; - struct sockaddr_un server; - -// fprintf(stderr, "kdeinit4: Warning, socket_file already exists!\n"); - // Create the socket stream - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s < 0) - { - perror("socket() failed"); - exit(255); - } - server.sun_family = AF_UNIX; - strcpy(server.sun_path, sock_file); - socklen = sizeof(server); - - if(connect(s, (struct sockaddr *)&server, socklen) == 0) - { - fprintf(stderr, "kdeinit4: Shutting down running client.\n"); - klauncher_header request_header; - request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; - request_header.arg_length = 0; - write(s, &request_header, sizeof(request_header)); - sleep(1); // Give it some time - } - close(s); - } - - // Delete any stale socket file (and symlink) - unlink(sock_file); -} - -static void init_kdeinit_socket() -{ - struct sockaddr_un sa; - QT_SOCKLEN_T socklen; - long options; - const QByteArray home_dir = qgetenv("HOME"); - int max_tries = 10; - if (home_dir.isEmpty()) - { - fprintf(stderr, "kdeinit4: Aborting. $HOME not set!"); - exit(255); - } - if (chdir(home_dir) != 0) { - fprintf(stderr, "kdeinit4: Aborting. Couldn't enter '%s'!", home_dir.constData()); - exit(255); - } - - { - QByteArray path = home_dir; - QByteArray readOnly = qgetenv("KDE_HOME_READONLY"); - if (access(path.data(), R_OK|W_OK)) - { - if (errno == ENOENT) - { - fprintf(stderr, "kdeinit4: Aborting. $HOME directory (%s) does not exist.\n", path.data()); - exit(255); - } - else if (readOnly.isEmpty()) - { - fprintf(stderr, "kdeinit4: Aborting. No write access to $HOME directory (%s).\n", path.data()); - exit(255); - } - } -#if 0 // obsolete in kde4. Should we check writing to another file instead? - path = qgetenv("ICEAUTHORITY"); - if (path.isEmpty()) - { - path = home_dir; - path += "/.ICEauthority"; - } - if (access(path.data(), R_OK|W_OK) && (errno != ENOENT)) - { - fprintf(stderr, "kdeinit4: Aborting. No write access to '%s'.\n", path.data()); - exit(255); - } -#endif - } - - shutdown_kdeinit(); - - // Create socket - d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0); - if (d.wrapper < 0) - { - perror("kdeinit4: Aborting. socket() failed"); - exit(255); - } - - options = fcntl(d.wrapper, F_GETFL); - if (options == -1) - { - perror("kdeinit4: Aborting. Can not make socket non-blocking"); - close(d.wrapper); - exit(255); - } - - if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1) - { - perror("kdeinit4: Aborting. Can not make socket non-blocking"); - close(d.wrapper); - exit(255); - } - - while (1) { - // Bind it - socklen = sizeof(sa); - memset(&sa, 0, socklen); - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, sock_file); - if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0) - { - if (max_tries == 0) { - perror("kdeinit4: Aborting. bind() failed"); - fprintf(stderr, "Could not bind to socket '%s'\n", sock_file); - close(d.wrapper); - exit(255); - } - max_tries--; - } else - break; - } - - // Set permissions - if (chmod(sock_file, 0600) != 0) - { - perror("kdeinit4: Aborting. Can not set permissions on socket"); - fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file); - unlink(sock_file); - close(d.wrapper); - exit(255); - } - - if(listen(d.wrapper, SOMAXCONN) < 0) - { - perror("kdeinit4: Aborting. listen() failed"); - unlink(sock_file); - close(d.wrapper); - exit(255); - } -} - -/* - * Read 'len' bytes from 'sock' into buffer. - * returns 0 on success, -1 on failure. - */ -static int read_socket(int sock, char *buffer, int len) -{ - ssize_t result; - int bytes_left = len; - while ( bytes_left > 0) - { - result = read(sock, buffer, bytes_left); - if (result > 0) - { - buffer += result; - bytes_left -= result; - } - else if (result == 0) - return -1; - else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) - return -1; - } - return 0; -} - -static void start_klauncher() -{ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher) < 0) { - perror("kdeinit4: socketpair() failed"); - exit(255); - } - char args[32]; - strcpy(args, "--fd="); - sprintf(args + 5, "%d", d.launcher[1]); - d.launcher_pid = launch( 2, "klauncher", args ); - close(d.launcher[1]); -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: Launched KLauncher, pid = %ld, result = %d\n", - (long) d.launcher_pid, d.result); -#endif -} - -static void launcher_died() -{ - if (!d.launcher_ok) - { - /* This is bad. */ - fprintf(stderr, "kdeinit4: Communication error with launcher. Exiting!\n"); - ::exit(255); - return; - } - - // KLauncher died... restart -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: KLauncher died unexpectedly.\n"); -#endif - // Make sure it's really dead. - if (d.launcher_pid) - { - kill(d.launcher_pid, SIGKILL); - sleep(1); // Give it some time - } - - d.launcher_ok = false; - d.launcher_pid = 0; - close(d.launcher[0]); - d.launcher[0] = -1; - - start_klauncher(); -} - -static bool handle_launcher_request(int sock, const char *who) -{ - (void)who; // for NDEBUG - - klauncher_header request_header; - char *request_data = 0L; - int result = read_socket(sock, (char *) &request_header, sizeof(request_header)); - if (result != 0) - { - return false; - } - - if ( request_header.arg_length != 0 ) - { - request_data = (char *) malloc(request_header.arg_length); - - result = read_socket(sock, request_data, request_header.arg_length); - if (result != 0) - { - free(request_data); - return false; - } - } - - //kDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd); - if (request_header.cmd == LAUNCHER_OK) - { - d.launcher_ok = true; - } - else if (request_header.arg_length && - ((request_header.cmd == LAUNCHER_EXEC_ASN) || - (request_header.cmd == LAUNCHER_EXEC))) - { - pid_t pid; - klauncher_header response_header; - long response_data; - long l; - memcpy( &l, request_data, sizeof( long )); - int argc = l; - const char *name = request_data + sizeof(long); - const char *args = name + strlen(name) + 1; - const char *cwd = 0; - int envc = 0; - const char *envs = 0; - const char *tty = 0; - int avoid_loops = 0; - const char *startup_id_str = "0"; // krazy:exclude=doublequote_chars - -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: Got %s '%s' from %s.\n", - commandToString(request_header.cmd), - name, who); -#endif - - const char *arg_n = args; - for(int i = 1; i < argc; i++) - { - arg_n = arg_n + strlen(arg_n) + 1; - } - - memcpy( &l, arg_n, sizeof( long )); - envc = l; - arg_n += sizeof(long); - envs = arg_n; - for(int i = 0; i < envc; i++) - { - arg_n = arg_n + strlen(arg_n) + 1; - } - memcpy( &l, arg_n, sizeof( long )); - avoid_loops = l; - arg_n += sizeof( long ); - - if( request_header.cmd == LAUNCHER_EXEC_ASN ) - { - startup_id_str = arg_n; - arg_n += strlen( startup_id_str ) + 1; - } - - if (request_header.arg_length > (arg_n - request_data)) - { - // Optional cwd - cwd = arg_n; arg_n += strlen(cwd) + 1; - } - - if ((arg_n - request_data) != request_header.arg_length) - { -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: EXEC request has invalid format.\n"); -#endif - free(request_data); - d.debug_wait = false; - return true; // sure? - } - - pid = launch( argc, name, args, cwd, envc, envs, - tty, avoid_loops, startup_id_str ); - - if (pid && (d.result == 0)) - { - response_header.cmd = LAUNCHER_OK; - response_header.arg_length = sizeof(response_data); - response_data = pid; - write(sock, &response_header, sizeof(response_header)); - write(sock, &response_data, response_header.arg_length); - - /* add new child to list */ - struct child *child = (struct child *) malloc(sizeof(struct child)); - child->pid = pid; - child->sock = dup(sock); - child->next = children; - children = child; - } - else - { - int l = d.errorMsg.length(); - if (l) l++; // Include trailing null. - response_header.cmd = LAUNCHER_ERROR; - response_header.arg_length = l; - write(sock, &response_header, sizeof(response_header)); - if (l) - write(sock, d.errorMsg.data(), l); - } - d.debug_wait = false; - } - else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV) - { - const char *env_name; - const char *env_value; - env_name = request_data; - env_value = env_name + strlen(env_name) + 1; - -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: Got SETENV '%s=%s' from %s.\n", env_name, env_value, who); -#endif - - if ( request_header.arg_length != - (int) (strlen(env_name) + strlen(env_value) + 2)) - { -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: SETENV request has invalid format.\n"); -#endif - free(request_data); - return true; // sure? - } - setenv( env_name, env_value, 1); - } - else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT) - { -#ifndef NDEBUG - fprintf(stderr,"kdeinit4: Got termination request (PID %ld).\n", (long) getpid()); -#endif - if (d.launcher_pid) { - kill(d.launcher_pid, SIGTERM); - d.launcher_pid = 0; - close(d.launcher[0]); - d.launcher[0] = -1; - } - unlink(sock_file); - if (children) { - close(d.wrapper); - d.wrapper = -1; -#ifndef NDEBUG - fprintf(stderr,"kdeinit4: Closed sockets, but not exiting until all children terminate.\n"); -#endif - } else { - raise(SIGTERM); - } - } - else if (request_header.cmd == LAUNCHER_DEBUG_WAIT) - { -#ifndef NDEBUG - fprintf(stderr,"kdeinit4: Debug wait activated.\n"); -#endif - d.debug_wait = true; - } - if (request_data) - free(request_data); - return true; -} - -static void handle_requests(pid_t waitForPid) -{ - int max_sock = d.deadpipe[0]; - if (d.wrapper > max_sock) - max_sock = d.wrapper; - if (d.launcher[0] > max_sock) - max_sock = d.launcher[0]; -#ifdef Q_WS_X11 - if (X11fd > max_sock) - max_sock = X11fd; -#endif - max_sock++; - - while(1) - { - fd_set rd_set; - fd_set wr_set; - fd_set e_set; - int result; - pid_t exit_pid; - int exit_status; - char c; - - /* Flush the pipe of death */ - while( read(d.deadpipe[0], &c, 1) == 1) - {} - - /* Handle dying children */ - do { - exit_pid = waitpid(-1, &exit_status, WNOHANG); - if (exit_pid > 0) - { -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: PID %ld terminated.\n", (long) exit_pid); -#endif - if (waitForPid && (exit_pid == waitForPid)) - return; - - if( WIFEXITED( exit_status )) // fix process return value - exit_status = WEXITSTATUS(exit_status); - else if( WIFSIGNALED(exit_status)) - exit_status = 128 + WTERMSIG( exit_status ); - child_died(exit_pid, exit_status); - - if (d.wrapper < 0 && !children) { -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: Last child terminated, exiting (PID %ld).\n", - (long) getpid()); -#endif - raise(SIGTERM); - } - } - } - while( exit_pid > 0); - - FD_ZERO(&rd_set); - FD_ZERO(&wr_set); - FD_ZERO(&e_set); - - if (d.launcher[0] >= 0) - FD_SET(d.launcher[0], &rd_set); - if (d.wrapper >= 0) - FD_SET(d.wrapper, &rd_set); - FD_SET(d.deadpipe[0], &rd_set); -#ifdef Q_WS_X11 - if(X11fd >= 0) FD_SET(X11fd, &rd_set); -#endif - - result = select(max_sock, &rd_set, &wr_set, &e_set, 0); - if (result < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - perror("kdeinit4: Aborting. select() failed"); - return; - } - - /* Handle wrapper request */ - if (d.wrapper >= 0 && FD_ISSET(d.wrapper, &rd_set)) - { - struct sockaddr_un client; - QT_SOCKLEN_T sClient = sizeof(client); - int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient); - if (sock >= 0) - { - d.accepted_fd = sock; - handle_launcher_request(sock, "wrapper"); - close(sock); - d.accepted_fd = -1; - } - } - - /* Handle launcher request */ - if (d.launcher[0] >= 0 && FD_ISSET(d.launcher[0], &rd_set)) - { - if (!handle_launcher_request(d.launcher[0], "launcher")) - launcher_died(); - if (waitForPid == d.launcher_pid) - return; - } - -#ifdef Q_WS_X11 - /* Look for incoming X11 events */ - if(X11fd >= 0 && FD_ISSET(X11fd,&rd_set)) { - if (X11display != 0) { - XEvent event_return; - while (XPending(X11display)) - XNextEvent(X11display, &event_return); - } - } -#endif - } -} - -static void kdeinit_library_path() -{ - QByteArray display = qgetenv(DISPLAY); - if (display.isEmpty()) - { -#if defined(Q_WS_X11) || defined(Q_WS_QWS) - fprintf(stderr, "kdeinit4: Aborting. $" DISPLAY " is not set.\n"); - exit(255); -#endif - } - int i; - if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) - display.truncate(i); - - display.replace(':','_'); - // WARNING, if you change the socket name, adjust kwrapper too - const QString socketFileName = QString::fromLatin1("kdeinit4_%1").arg(QLatin1String(display)); - QByteArray socketName = QFile::encodeName(KStandardDirs::locateLocal("tmp", socketFileName, *s_instance)); - if (socketName.length() >= MAX_SOCK_FILE) - { - fprintf(stderr, "kdeinit4: Aborting. Socket name will be too long:\n"); - fprintf(stderr, " '%s'\n", socketName.data()); - exit(255); - } - strcpy(sock_file, socketName.data()); -} - -int kdeinit_xio_errhandler( Display *disp ) -{ - // disp is 0L when KDE shuts down. We don't want those warnings then. - if ( disp ) - kWarning() << "Fatal IO error: client killed"; - - if (sock_file[0]) - { - // Delete any stale socket file - unlink(sock_file); - } - - // Don't kill our children in suicide mode, they may still be in use - if (d.suicide) - { - if (d.launcher_pid) - kill(d.launcher_pid, SIGTERM); - exit( 0 ); - } - - if ( disp ) - kWarning() << "Sending SIGHUP to children."; - - // this should remove all children we started - KDE_signal(SIGHUP, SIG_IGN); - kill(0, SIGHUP); - - sleep(2); - - if ( disp ) - kWarning() << "Sending SIGTERM to children."; - - // And if they don't listen to us, this should work - KDE_signal(SIGTERM, SIG_IGN); - kill(0, SIGTERM); - - if ( disp ) - kWarning() << "Exit."; - - exit( 0 ); - return 0; -} - -#ifdef Q_WS_X11 -int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err ) -{ -#ifndef NDEBUG - char errstr[256]; - // kdeinit almost doesn't use X, and therefore there shouldn't be any X error - XGetErrorText( dpy, err->error_code, errstr, 256 ); - fprintf(stderr, "kdeinit4(%d) : KDE detected X Error: %s %d\n" - " Major opcode: %d\n" - " Minor opcode: %d\n" - " Resource id: 0x%lx\n", - getpid(), errstr, err->error_code, err->request_code, err->minor_code, err->resourceid ); - - //kDebug() << kBacktrace(); - -#else - Q_UNUSED(dpy); - Q_UNUSED(err); -#endif - return 0; -} -#endif - -#ifdef Q_WS_X11 -// needs to be done sooner than initXconnection() because of also opening -// another X connection for startup notification purposes -static void setupX() -{ - XSetIOErrorHandler(kdeinit_xio_errhandler); - XSetErrorHandler(kdeinit_x_errhandler); -} - -// Borrowed from kdebase/kaudio/kaudioserver.cpp -static int initXconnection() -{ - X11display = XOpenDisplay(NULL); - if ( X11display != 0 ) { - XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \ - 0, - BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)), - BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) ); -#ifndef NDEBUG - fprintf(stderr, "kdeinit4: opened connection to %s\n", DisplayString(X11display)); -#endif - int fd = XConnectionNumber( X11display ); - int on = 1; - (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on)); - return fd; - } else - fprintf(stderr, "kdeinit4: Can not connect to the X Server.\n" \ - "kdeinit4: Might not terminate at end of session.\n"); - - return -1; -} -#endif - -extern "C" { - -static void secondary_child_handler(int) -{ - waitpid(-1, 0, WNOHANG); -} - -} - -int main(int argc, char **argv) -{ - setlocale (LC_ALL, ""); - setlocale (LC_NUMERIC, "C"); - - pid_t pid; - bool do_fork = true; - int launch_klauncher = 1; - int do_shutdown = 0; - d.suicide = false; - - // Check arguments first... - for(int i = 0; i < argc; i++) - { - if (strcmp(argv[i], "--no-klauncher") == 0) - launch_klauncher = 0; - if (strcmp(argv[i], "--no-fork") == 0) - do_fork = false; - if (strcmp(argv[i], "--suicide") == 0) - d.suicide = true; - if (strcmp(argv[i], "--shutdown") == 0) - do_shutdown = 1; - if (strcmp(argv[i], "--version") == 0) - { - printf("Katie: %s\n", qVersion()); - printf("KDE: %s\n", KDE_VERSION_STRING); - exit(0); - } - if (strcmp(argv[i], "--help") == 0) - { - printf("Usage: kdeinit4 [options]\n"); - printf(" --no-fork Do not fork\n"); - printf(" --no-klauncher Do not start klauncher\n"); - printf(" --suicide Terminate when no KDE applications are left running\n"); - printf(" --version Show version information\n"); - printf(" --shutdown Shutdown running kdeinit4 instance\n"); - exit(0); - } - } - - if (do_shutdown) { - KDE_signal(SIGPIPE, SIG_IGN); - s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration); - kdeinit_library_path(); - shutdown_kdeinit(); - return 0; - } - - cleanup_fds(); - - // Redirect stdout to stderr. We have no reason to use stdout anyway. - // This minimizes our impact on commands used in pipes. - (void)dup2(2, 1); - - if (do_fork) { - if (pipe(d.initpipe) != 0) { - perror("kdeinit4: pipe failed"); - return 1; - } - - // Fork here and let parent process exit. - // Parent process may only exit after all required services have been - // launched. (dcopserver/klauncher and services which start with '+') - KDE_signal( SIGCHLD, secondary_child_handler); - if (fork() > 0) // Go into background - { - close(d.initpipe[1]); - d.initpipe[1] = -1; - // wait till init is complete - char c; - while( read(d.initpipe[0], &c, 1) < 0) - ; - // then exit; - close(d.initpipe[0]); - d.initpipe[0] = -1; - return 0; - } - close(d.initpipe[0]); - d.initpipe[0] = -1; - } - - // Make process group leader (for shutting down children later) - setsid(); - - // Create our instance - s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration); - - // Prepare to change process name -#ifndef SKIP_PROCTITLE - proctitle_init(argc, argv); -#endif - - kdeinit_library_path(); - // Don't make our instance the global instance - // (do it only after kdeinit_library_path, that one indirectly uses KConfig, - // which seems to be buggy and always use KGlobal instead of the matching KComponentData) - Q_ASSERT(!KGlobal::hasMainComponent()); - KApplication::loadedByKdeinit = true; - - d.maxname = strlen(argv[0]); - d.launcher_pid = 0; - d.wrapper = -1; - d.accepted_fd = -1; - d.debug_wait = false; - d.launcher_ok = false; - children = NULL; - init_signals(); -#ifdef Q_WS_X11 - setupX(); -#endif - - // Create socket for incoming wrapper requests - init_kdeinit_socket(); - - if (launch_klauncher) - { - start_klauncher(); - handle_requests(d.launcher_pid); // Wait for klauncher to be ready - } - -#ifdef Q_WS_X11 - X11fd = initXconnection(); -#endif - -#ifndef SKIP_PROCTITLE - proctitle_set("kdeinit4 Running..."); -#endif - - if (d.initpipe[1] != -1) - { - char c = 0; - write(d.initpipe[1], &c, 1); // Kdeinit is started. - close(d.initpipe[1]); - d.initpipe[1] = -1; - } - - handle_requests(0); - - return 0; -} - - diff --git a/kinit/klauncher.cpp b/kinit/klauncher.cpp index d17cba9c..7c563b9f 100644 --- a/kinit/klauncher.cpp +++ b/kinit/klauncher.cpp @@ -1,1217 +1,97 @@ -/* - This file is part of the KDE libraries - Copyright (c) 1999 Waldo Bastian +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ -#define QT_NO_CAST_FROM_ASCII - #include "klauncher.h" -#include "klauncher_cmds.h" #include "klauncher_adaptor.h" +#include "kaboutdata.h" +#include "kdeversion.h" +#include "kapplication.h" +#include "kcmdlineargs.h" +#include "kdebug.h" -#include +#include +#include +#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_WS_X11 -#include -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// Dispose slaves after being idle for SLAVE_MAX_IDLE seconds -#define SLAVE_MAX_IDLE 30 - -// #define KLAUNCHER_VERBOSE_OUTPUT - -#ifdef KLAUNCHER_VERBOSE_OUTPUT -static const char* const s_DBusStartupTypeToString[] = - { "DBusNone", "DBusUnique", "DBusMulti", "DBusWait", "ERROR" }; -#endif - -using namespace KIO; - -IdleSlave::IdleSlave(QObject *parent) +KLauncher::KLauncher(QObject *parent) : QObject(parent) { - QObject::connect(&mConn, SIGNAL(readyRead()), this, SLOT(gotInput())); - // Send it a SLAVE_STATUS command. - mConn.send( CMD_SLAVE_STATUS ); - mPid = 0; - mBirthDate = time(0); - mOnHold = false; -} + m_adaptor = new KLauncherAdaptor(this); -void -IdleSlave::gotInput() -{ - int cmd; - QByteArray data; - if (mConn.read( &cmd, data) == -1) - { - // Communication problem with slave. - // kError(7016) << "No communication with slave."; - deleteLater(); - } - else if (cmd == MSG_SLAVE_ACK) - { - deleteLater(); - } - else if (cmd != MSG_SLAVE_STATUS) - { - kError(7016) << "Unexpected data from slave."; - deleteLater(); - } - else - { - QDataStream stream( data ); - qint64 pid; - QByteArray protocol; - QString host; - qint8 b; - stream >> pid >> protocol >> host >> b; - // Overload with (bool) onHold, (KUrl) url. - if (!stream.atEnd()) - { - KUrl url; - stream >> url; - mOnHold = true; - mUrl = url; - } - - mPid = static_cast(pid); - mConnected = (b != 0); - mProtocol = QString::fromLatin1(protocol); - mHost = host; - emit statusUpdate(this); - } -} - -void -IdleSlave::connect(const QString &app_socket) -{ - QByteArray data; - QDataStream stream( &data, QIODevice::WriteOnly); - stream << app_socket; - mConn.send( CMD_SLAVE_CONNECT, data ); - // Timeout! -} - -void -IdleSlave::reparseConfiguration() -{ - mConn.send( CMD_REPARSECONFIGURATION ); -} - -bool -IdleSlave::match(const QString &protocol, const QString &host, bool needConnected) const -{ - if (mOnHold || protocol != mProtocol) { - return false; - } - if (host.isEmpty()) { - return true; - } - return (host == mHost) && (!needConnected || mConnected); -} - -bool -IdleSlave::onHold(const KUrl &url) const -{ - if (!mOnHold) return false; - return (url == mUrl); -} - -int -IdleSlave::age(time_t now) const -{ - return (int) difftime(now, mBirthDate); -} - -static KLauncher* g_klauncher_self = NULL; - - -// From qcore_unix_p.h. We could also port to QLocalSocket :) -#define K_EINTR_LOOP(var, cmd) \ - do { \ - var = cmd; \ - } while (var == -1 && errno == EINTR) - -ssize_t kde_safe_write(int fd, const void *buf, size_t count) -{ - ssize_t ret = 0; - K_EINTR_LOOP(ret, QT_WRITE(fd, buf, count)); - if (ret < 0) - kWarning() << "write failed:" << strerror(errno); - return ret; -} - -KLauncher::KLauncher(int _kdeinitSocket) - : QObject(0), - kdeinitSocket(_kdeinitSocket) -{ -#ifdef Q_WS_X11 - mCached_dpy = NULL; -#endif - Q_ASSERT( g_klauncher_self == NULL ); - g_klauncher_self = this; - - mAutoTimer.setSingleShot(true); - new KLauncherAdaptor(this); - QDBusConnection::sessionBus().registerObject(QLatin1String("/KLauncher"), this); // same as ktoolinvocation.cpp - - connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart())); - connect(QDBusConnection::sessionBus().interface(), - SIGNAL(serviceOwnerChanged(QString,QString,QString)), - SLOT(slotNameOwnerChanged(QString,QString,QString))); - - mConnectionServer.listenForRemote(); - connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave())); - if (!mConnectionServer.isListening()) - { - // Severe error! - kDebug() << "Fatal error, can't create tempfile!"; - ::_exit(1); - } - - connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout())); - - kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read); - connect(kdeinitNotifier, SIGNAL(activated(int)), - this, SLOT(slotKDEInitData(int))); - kdeinitNotifier->setEnabled( true ); - lastRequest = 0; - bProcessingQueue = false; - - mSlaveDebug = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_DEBUG_WAIT")); - if (!mSlaveDebug.isEmpty()) - { - kWarning() << "Running in slave-debug mode for slaves of protocol:" << qPrintable(mSlaveDebug); - } - mSlaveValgrind = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND")); - if (!mSlaveValgrind.isEmpty()) - { - mSlaveValgrindSkin = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND_SKIN")); - kWarning() << "Running slaves through valgrind for slaves of protocol:" << qPrintable(mSlaveValgrind); - } - klauncher_header request_header; - request_header.cmd = LAUNCHER_OK; - request_header.arg_length = 0; - kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); + QDBusConnection session = QDBusConnection::sessionBus(); + session.registerObject("/KLauncher", this); + session.registerService("org.kde.klauncher"); } KLauncher::~KLauncher() { - close(); - g_klauncher_self = NULL; + QDBusConnection session = QDBusConnection::sessionBus(); + session.unregisterObject("/KLauncher"); + session.unregisterService("org.kde.klauncher"); } -void KLauncher::close() +void KLauncher::setSessionManager(const QByteArray &sessionmanager) { -#ifdef Q_WS_X11 - if( mCached_dpy != NULL ) - { - XCloseDisplay( mCached_dpy ); - mCached_dpy = NULL; - } -#endif -} - -void -KLauncher::destruct() -{ - if (g_klauncher_self) - g_klauncher_self->close(); - // We don't delete the app here, that's intentional. - ::_exit(255); -} - -void KLauncher::setLaunchEnv(const QString &name, const QString &value) -{ - klauncher_header request_header; - QByteArray requestData; - requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0'); - request_header.cmd = LAUNCHER_SETENV; - request_header.arg_length = requestData.size(); - kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); - kde_safe_write(kdeinitSocket, requestData.data(), request_header.arg_length); -} - -/* - * Read 'len' bytes from 'sock' into buffer. - * returns -1 on failure, 0 on no data. - */ -static int -read_socket(int sock, char *buffer, int len) -{ - ssize_t result; - int bytes_left = len; - while (bytes_left > 0) { - // in case we get a request to start an application and data arrive - // to kdeinitSocket at the same time, requestStart() will already - // call slotKDEInitData(), so we must check there's still something - // to read, otherwise this would block - - // Same thing if kdeinit dies without warning. - - fd_set in; - timeval tm = { 30, 0 }; // 30 seconds timeout, so we're not stuck in case kdeinit dies on us - FD_ZERO ( &in ); - FD_SET( sock, &in ); - select( sock + 1, &in, 0, 0, &tm ); - if( !FD_ISSET( sock, &in )) { - kDebug(7016) << "read_socket" << sock << "nothing to read, kdeinit4 must be dead"; - return -1; - } - - result = read(sock, buffer, bytes_left); - if (result > 0) - { - buffer += result; - bytes_left -= result; - } - else if (result == 0) - return -1; - else if ((result == -1) && (errno != EINTR)) - return -1; - } - return 0; -} - -void -KLauncher::slotKDEInitData(int) -{ - klauncher_header request_header; - QByteArray requestData; - - int result = read_socket(kdeinitSocket, (char *) &request_header, - sizeof( request_header)); - if (result == -1) - { - kDebug(7016) << "Exiting on read_socket errno:" << errno; - KDE_signal( SIGHUP, SIG_IGN); - KDE_signal( SIGTERM, SIG_IGN); - destruct(); // Exit! - } - requestData.resize(request_header.arg_length); - result = read_socket(kdeinitSocket, (char *) requestData.data(), - request_header.arg_length); - - processRequestReturn(request_header.cmd,requestData); -} - -void KLauncher::processRequestReturn(int status, const QByteArray &requestData) -{ - if (status == LAUNCHER_CHILD_DIED) - { - long *request_data = (long *) requestData.data(); - processDied(request_data[0], request_data[1]); - return; - } - if (lastRequest && (status == LAUNCHER_OK)) - { - long *request_data = (long *) requestData.data(); - lastRequest->pid = (pid_t) (*request_data); - kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid << - ") up and running."; - switch(lastRequest->dbus_startup_type) - { - case KService::DBusNone: - lastRequest->status = KLaunchRequest::Running; - break; - case KService::DBusUnique: - case KService::DBusWait: - case KService::DBusMulti: - lastRequest->status = KLaunchRequest::Launching; - break; - } - lastRequest = 0; - return; - } - if (lastRequest && (status == LAUNCHER_ERROR)) - { - lastRequest->status = KLaunchRequest::Error; - kDebug(7016) << lastRequest->name << " failed."; - if (!requestData.isEmpty()) - lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data()); - lastRequest = 0; - return; - } - - kWarning(7016)<< "Unexpected request return" << (unsigned int) status; -} - -void -KLauncher::processDied(pid_t pid, long exitStatus) -{ -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << pid << "exitStatus=" << exitStatus; -#else - Q_UNUSED(exitStatus); - // We should probably check the exitStatus for the uniqueapp case? -#endif - foreach (KLaunchRequest *request, requestList) - { -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << " had pending request" << request->pid; -#endif - if (request->pid == pid) - { - if (request->dbus_startup_type == KService::DBusWait) - request->status = KLaunchRequest::Done; - else if ((request->dbus_startup_type == KService::DBusUnique) - && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { - request->status = KLaunchRequest::Running; -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << pid << "running as a unique app"; -#endif - } else { - request->status = KLaunchRequest::Error; -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << pid << "died, requestDone. status=" << request->status; -#endif - } - requestDone(request); - return; - } - } -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "found no pending requests for PID" << pid; -#endif -} - -static bool matchesPendingRequest(const QString& appId, const QString& pendingAppId) -{ - // appId just registered, e.g. org.koffice.kword-12345 - // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for. - - const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char('-'))); // strip out the -12345 if present. - - //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId; - - if (pendingAppId.startsWith(QLatin1String("*."))) { - const QString pendingName = pendingAppId.mid(2); - const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char('.'))+1); - //kDebug() << "appName=" << appName; - return appName == pendingName; - } - - return newAppId == pendingAppId; -} - -void -KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner, - const QString &newOwner) -{ - Q_UNUSED(oldOwner); - if (appId.isEmpty() || newOwner.isEmpty()) - return; - -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "new app" << appId; -#endif - foreach (KLaunchRequest *request, requestList) - { - if (request->status != KLaunchRequest::Launching) - continue; - -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "had pending request" << request->name << s_DBusStartupTypeToString[request->dbus_startup_type] << "dbus_name" << request->dbus_name << request->tolerant_dbus_name; -#endif - // For unique services check the requested service name first - if (request->dbus_startup_type == KService::DBusUnique) { - if ((appId == request->dbus_name) || // just started - QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { // was already running - request->status = KLaunchRequest::Running; -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "OK, unique app" << request->dbus_name << "is running"; -#endif - requestDone(request); - continue; - } else { -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "unique app" << request->dbus_name << "not running yet"; -#endif - } - } - - const QString rAppId = !request->tolerant_dbus_name.isEmpty() ? request->tolerant_dbus_name : request->dbus_name; -#ifdef KLAUNCHER_VERBOSE_OUTPUT - //kDebug(7016) << "using" << rAppId << "for matching"; -#endif - if (rAppId.isEmpty()) - continue; - - if (matchesPendingRequest(appId, rAppId)) { -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "ok, request done"; -#endif - request->dbus_name = appId; - request->status = KLaunchRequest::Running; - requestDone(request); - continue; - } - } -} - -void -KLauncher::autoStart(int phase) -{ - if( mAutoStart.phase() >= phase ) - return; - mAutoStart.setPhase(phase); - if (phase == 0) - mAutoStart.loadAutoStartList(); - mAutoTimer.start(0); -} - -void -KLauncher::slotAutoStart() -{ - KService::Ptr s; - do - { - QString service = mAutoStart.startService(); - if (service.isEmpty()) - { - // Done - if( !mAutoStart.phaseDone()) - { - mAutoStart.setPhaseDone(); - switch( mAutoStart.phase()) - { - case 0: - emit autoStart0Done(); - break; - case 1: - emit autoStart1Done(); - break; - case 2: - emit autoStart2Done(); - break; - } - } - return; - } - s = new KService(service); - } - while (!start_service(s, QStringList(), QStringList(), "0", false, true, QDBusMessage())); - // Loop till we find a service that we can start. -} - -void -KLauncher::requestDone(KLaunchRequest *request) -{ - if ((request->status == KLaunchRequest::Running) || - (request->status == KLaunchRequest::Done)) - { - requestResult.result = 0; - requestResult.dbusName = request->dbus_name; - requestResult.error = QString::fromLatin1(""); // not null, cf assert further down - requestResult.pid = request->pid; - } - else - { - requestResult.result = 1; - requestResult.dbusName.clear(); - requestResult.error = i18n("KDEInit could not launch '%1'", request->name); - if (!request->errorMsg.isEmpty()) - requestResult.error += QString::fromLatin1(":\n") + request->errorMsg; - requestResult.pid = 0; - -#ifdef Q_WS_X11 - if (!request->startup_dpy.isEmpty()) - { - Display* dpy = NULL; - if( (mCached_dpy != NULL) && - (request->startup_dpy == XDisplayString( mCached_dpy ))) - dpy = mCached_dpy; - if( dpy == NULL ) - dpy = XOpenDisplay(request->startup_dpy); - if( dpy ) - { - KStartupInfoId id; - id.initId(request->startup_id); - KStartupInfo::sendFinishX( dpy, id ); - if( mCached_dpy != dpy && mCached_dpy != NULL ) - XCloseDisplay( mCached_dpy ); - mCached_dpy = dpy; - } - } -#endif - } - - if (request->autoStart) - { - mAutoTimer.start(0); - } - - if (request->transaction.type() != QDBusMessage::InvalidMessage) - { - if ( requestResult.dbusName.isNull() ) // null strings can't be sent - requestResult.dbusName.clear(); - Q_ASSERT( !requestResult.error.isNull() ); - QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result - << requestResult.dbusName - << requestResult.error - << static_cast(requestResult.pid))); - } -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "removing done request" << request->name << "PID" << request->pid; -#endif - - requestList.removeAll( request ); - delete request; -} - -static void appendLong(QByteArray &ba, long l) -{ - const int sz = ba.size(); - ba.resize(sz + sizeof(long)); - memcpy(ba.data() + sz, &l, sizeof(long)); -} - -void -KLauncher::requestStart(KLaunchRequest *request) -{ - requestList.append( request ); - // Send request to kdeinit. - klauncher_header request_header; - QByteArray requestData; - requestData.reserve(1024); - - appendLong(requestData, request->arg_list.count() + 1); - requestData.append(request->name.toLocal8Bit()); - requestData.append('\0'); - foreach (const QString &arg, request->arg_list) - requestData.append(arg.toLocal8Bit()).append('\0'); - appendLong(requestData, request->envs.count()); - foreach (const QString &env, request->envs) - requestData.append(env.toLocal8Bit()).append('\0'); - appendLong(requestData, 0); // avoid_loops, always false here -#ifdef Q_WS_X11 - bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0"; - if( startup_notify ) - requestData.append(request->startup_id).append('\0'); -#endif - if (!request->cwd.isEmpty()) - requestData.append(QFile::encodeName(request->cwd)).append('\0'); - -#ifdef Q_WS_X11 - request_header.cmd = startup_notify ? LAUNCHER_EXEC_ASN : LAUNCHER_EXEC; -#else - request_header.cmd = LAUNCHER_EXEC; -#endif - request_header.arg_length = requestData.length(); - -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "Asking kdeinit to start" << request->name << request->arg_list - << "cmd=" << commandToString(request_header.cmd); -#endif - - kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); - kde_safe_write(kdeinitSocket, requestData.data(), requestData.length()); - - // Wait for pid to return. - lastRequest = request; - do { - slotKDEInitData( kdeinitSocket ); - } - while (lastRequest != 0); -} - -void KLauncher::exec_blind(const QString &name, const QStringList &arg_list) -{ - KLaunchRequest *request = new KLaunchRequest; - request->autoStart = false; - request->name = name; - request->arg_list = arg_list; - request->dbus_startup_type = KService::DBusNone; - request->pid = 0; - request->status = KLaunchRequest::Launching; - request->envs = QStringList(); - - requestStart(request); - // We don't care about this request any longer.... - requestDone(request); -} - -bool -KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, - const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) -{ - KService::Ptr service; - // Find service - const QFileInfo fi(serviceName); - if (fi.isAbsolute() && fi.exists()) - { - // Full path - service = new KService(serviceName); - } - else - { - service = KService::serviceByDesktopPath(serviceName); - // TODO? - //if (!service) - // service = KService::serviceByStorageId(serviceName); // This method should be named start_service_by_storage_id ideally... - } - if (!service) - { - requestResult.result = ENOENT; - requestResult.error = i18n("Could not find service '%1'.", serviceName); - cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any - return false; - } - return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); -} - -bool -KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, - const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) -{ - KService::Ptr service = KService::serviceByDesktopName(serviceName); - if (!service) - { - requestResult.result = ENOENT; - requestResult.error = i18n("Could not find service '%1'.", serviceName); - cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any - return false; - } - return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); -} - -bool -KLauncher::start_service(KService::Ptr service, const QStringList &_urls, - const QStringList &envs, const QByteArray &startup_id, - bool blind, bool autoStart, QDBusMessage msg) -{ - QStringList urls = _urls; - bool runPermitted = KDesktopFile::isAuthorizedDesktopFile(service->entryPath()); - - if (!service->isValid() || !runPermitted) - { - requestResult.result = ENOEXEC; - if (service->isValid()) - requestResult.error = i18n("Service '%1' must be executable to run.", service->entryPath()); - else - requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); - cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any - return false; - } - KLaunchRequest *request = new KLaunchRequest; - request->autoStart = autoStart; - - if ((urls.count() > 1) && !service->allowMultipleFiles()) - { - // We need to launch the application N times. That sucks. - // We ignore the result for application 2 to N. - // For the first file we launch the application in the - // usual way. The reported result is based on this - // application. - foreach(const QString &it, urls) { - QByteArray startup_id2 = startup_id; - const QStringList singleUrl(it); - if( !startup_id2.isEmpty() && startup_id2 != "0" ) - startup_id2 = "0"; // can't use the same startup_id several times // krazy:exclude=doublequote_chars - start_service( service, singleUrl, envs, startup_id2, true, false, msg); - } - QString firstURL = *(urls.begin()); - urls.clear(); - urls.append(firstURL); - } - createArgs(request, service, urls); - - // We must have one argument at least! - if (!request->arg_list.count()) - { - requestResult.result = ENOEXEC; - requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); - delete request; - cancel_service_startup_info( NULL, startup_id, envs ); - return false; - } - - request->name = request->arg_list.takeFirst(); - - if (request->name.endsWith(QLatin1String("/kioexec"))) { - // Special case for kioexec; if createArgs said we were going to use it, - // then we have to expect a kioexec-PID, not a org.kde.finalapp... - // Testcase: konqueror www.kde.org, RMB on link, open with, kruler. - - request->dbus_startup_type = KService::DBusMulti; - request->dbus_name = QString::fromLatin1("org.kde.kioexec"); - } else { - request->dbus_startup_type = service->dbusStartupType(); - - if ((request->dbus_startup_type == KService::DBusUnique) || - (request->dbus_startup_type == KService::DBusMulti)) { - const QVariant v = service->property(QLatin1String("X-DBUS-ServiceName")); - if (v.isValid()) { - request->dbus_name = v.toString(); - } - if (request->dbus_name.isEmpty()) { - const QString binName = KRun::binaryName(service->exec(), true); - request->dbus_name = QString::fromLatin1("org.kde.") + binName; - request->tolerant_dbus_name = QString::fromLatin1("*.") + binName; - } - } - } - -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "name=" << request->name << "dbus_name=" << request->dbus_name - << "startup type=" << s_DBusStartupTypeToString[request->dbus_startup_type]; -#endif - - request->pid = 0; - request->envs = envs; - send_service_startup_info( request, service, startup_id, envs ); - - // Request will be handled later. - if (!blind && !autoStart) - { - msg.setDelayedReply(true); - request->transaction = msg; - } - queueRequest(request); - return true; -} - -void -KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray& startup_id, - const QStringList &envs ) -{ -#ifdef Q_WS_X11 - request->startup_id = "0";// krazy:exclude=doublequote_chars - if (startup_id == "0") - return; - bool silent; - QByteArray wmclass; - if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass )) - return; - KStartupInfoId id; - id.initId(startup_id); - QByteArray dpy_str; - foreach (const QString &env, envs) { - if (env.startsWith(QLatin1String("DISPLAY="))) - dpy_str = env.mid(8).toLocal8Bit(); - } - Display* dpy = NULL; - if (!dpy_str.isEmpty() && mCached_dpy != NULL && dpy_str != XDisplayString(mCached_dpy)) - dpy = mCached_dpy; - if (dpy == NULL) - dpy = XOpenDisplay(dpy_str); - request->startup_id = id.id(); - if (dpy == NULL) { - cancel_service_startup_info( request, startup_id, envs ); - return; - } - - request->startup_dpy = dpy_str; - - KStartupInfoData data; - data.setName( service->name()); - data.setIcon( service->icon()); - data.setDescription( i18n( "Launching %1" , service->name())); - if( !wmclass.isEmpty()) - data.setWMClass( wmclass ); - if( silent ) - data.setSilent( KStartupInfoData::Yes ); - data.setApplicationId( service->entryPath()); - // the rest will be sent by kdeinit - KStartupInfo::sendStartupX( dpy, id, data ); - if( mCached_dpy != dpy && mCached_dpy != NULL ) - XCloseDisplay( mCached_dpy ); - mCached_dpy = dpy; - return; -#else - return; -#endif -} - -void -KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QByteArray& startup_id, - const QStringList &envs ) -{ -#ifdef Q_WS_X11 - if( request != NULL ) - request->startup_id = "0"; // krazy:exclude=doublequote_chars - if( !startup_id.isEmpty() && startup_id != "0" ) - { - QByteArray dpy_bytes; - foreach (const QString &env, envs) { - if (env.startsWith(QLatin1String("DISPLAY="))) - dpy_bytes = env.mid(8).toLatin1(); - } - Display* dpy = NULL; - if( !dpy_bytes.isEmpty() && mCached_dpy != NULL - && dpy_bytes != XDisplayString( mCached_dpy ) ) - dpy = mCached_dpy; - if( dpy == NULL ) - dpy = XOpenDisplay( dpy_bytes.constData() ); - if( dpy == NULL ) - return; - KStartupInfoId id; - id.initId(startup_id); - KStartupInfo::sendFinishX( dpy, id ); - if( mCached_dpy != dpy && mCached_dpy != NULL ) - XCloseDisplay( mCached_dpy ); - mCached_dpy = dpy; - } -#endif -} - -bool -KLauncher::kdeinit_exec(const QString &app, const QStringList &args, - const QString& workdir, const QStringList &envs, - const QString &startup_id, bool wait, QDBusMessage msg) -{ - KLaunchRequest *request = new KLaunchRequest; - request->autoStart = false; - request->arg_list = args; - request->name = app; - if (wait) - request->dbus_startup_type = KService::DBusWait; - else - request->dbus_startup_type = KService::DBusNone; - request->pid = 0; -#ifdef Q_WS_X11 - request->startup_id = startup_id.toLocal8Bit(); -#endif - request->envs = envs; - request->cwd = workdir; -#ifdef Q_WS_X11 - if (!app.endsWith(QLatin1String("kbuildsycoca4"))) { // avoid stupid loop - // Find service, if any - strip path if needed - const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char('/')) + 1); - KService::Ptr service = KService::serviceByDesktopName(desktopName); - if (service) - send_service_startup_info(request, service, - request->startup_id, envs); - else // no .desktop file, no startup info - cancel_service_startup_info(request, request->startup_id, envs); - } -#endif - msg.setDelayedReply(true); - request->transaction = msg; - queueRequest(request); - return true; -} - -void -KLauncher::queueRequest(KLaunchRequest *request) -{ - requestQueue.append( request ); - if (!bProcessingQueue) - { - bProcessingQueue = true; - QTimer::singleShot(0, this, SLOT(slotDequeue())); - } -} - -void -KLauncher::slotDequeue() -{ - do { - KLaunchRequest *request = requestQueue.takeFirst(); - // process request - request->status = KLaunchRequest::Launching; - requestStart(request); - if (request->status != KLaunchRequest::Launching) - { - // Request handled. -#ifdef KLAUNCHER_VERBOSE_OUTPUT - kDebug(7016) << "Request handled already"; -#endif - requestDone( request ); - continue; - } - } while(requestQueue.count()); - bProcessingQueue = false; -} - -void -KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service , - const QStringList &urls) -{ - const QStringList params = KRun::processDesktopExec(*service, urls); - - for(QStringList::ConstIterator it = params.begin(); - it != params.end(); ++it) - { - request->arg_list.append(*it); - } - - const QString& path = service->path(); - if (!path.isEmpty()) { - request->cwd = path; - } else if (!urls.isEmpty()) { - const KUrl url(urls.first()); - if (url.isLocalFile()) { - request->cwd = url.directory(); - } - } -} - -///// IO-Slave functions - -pid_t -KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket) -{ - IdleSlave *slave = 0; - foreach (IdleSlave *p, mSlaveList) - { - if (p->onHold(url)) - { - slave = p; - break; - } - } - if (slave) - { - mSlaveList.removeAll(slave); - slave->connect(app_socket); - return slave->pid(); - } - return 0; -} - -pid_t -KLauncher::requestSlave(const QString &protocol, - const QString &host, - const QString &app_socket, - QString &error) -{ - IdleSlave *slave = 0; - foreach (IdleSlave *p, mSlaveList) - { - if (p->match(protocol, host, true)) - { - slave = p; - break; - } - } - if (!slave) - { - foreach (IdleSlave *p, mSlaveList) - { - if (p->match(protocol, host, false)) - { - slave = p; - break; - } - } - } - if (!slave) - { - foreach (IdleSlave *p, mSlaveList) - { - if (p->match(protocol, QString(), false)) - { - slave = p; - break; - } - } - } - if (slave) - { - mSlaveList.removeAll(slave); - slave->connect(app_socket); - return slave->pid(); - } - - QString name = KProtocolInfo::exec(protocol); - if (name.isEmpty()) - { - error = i18n("Unknown protocol '%1'.\n", protocol); - return 0; - } - - QStringList arg_list; - arg_list.append(protocol); - arg_list.append(mConnectionServer.address()); - arg_list.append(app_socket); - - kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol - << " args=" << arg_list; - -#ifdef Q_OS_UNIX - if (mSlaveDebug == protocol) - { - klauncher_header request_header; - request_header.cmd = LAUNCHER_DEBUG_WAIT; - request_header.arg_length = 0; - kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); - } - if (mSlaveValgrind == protocol) { - KPluginLoader lib(name, KGlobal::mainComponent()); - arg_list.prepend(lib.fileName()); - arg_list.prepend(KStandardDirs::findExe(QString::fromLatin1("kioslave"))); - name = QString::fromLatin1("valgrind"); - - if (!mSlaveValgrindSkin.isEmpty()) { - arg_list.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin); - } else { - arg_list.prepend(QLatin1String("--tool=memcheck")); - } - } -#endif - KLaunchRequest *request = new KLaunchRequest; - request->autoStart = false; - request->name = name; - request->arg_list = arg_list; - request->dbus_startup_type = KService::DBusNone; - request->pid = 0; -#ifdef Q_WS_X11 - request->startup_id = "0"; // krazy:exclude=doublequote_chars -#endif - request->status = KLaunchRequest::Launching; - requestStart(request); - pid_t pid = request->pid; - -// kDebug(7016) << "Slave launched, pid = " << pid; - - // We don't care about this request any longer.... - requestDone(request); - if (!pid) - { - error = i18n("Error loading '%1'.\n", name); - } - return pid; -} - -bool KLauncher::checkForHeldSlave(const QString &url) -{ - Q_FOREACH (const IdleSlave *p, mSlaveList) { - if (p->onHold(url)) { - return true; - } - } - return false; -} - -void -KLauncher::waitForSlave(pid_t pid, QDBusMessage msg) -{ - foreach (const IdleSlave *slave, mSlaveList) - { - if (slave->pid() == pid) - return; // Already here. - } - SlaveWaitRequest *waitRequest = new SlaveWaitRequest; - msg.setDelayedReply(true); - waitRequest->transaction = msg; - waitRequest->pid = pid; - mSlaveWaitRequest.append(waitRequest); -} - -void -KLauncher::acceptSlave() -{ - IdleSlave *slave = new IdleSlave(this); - mConnectionServer.setNextPendingConnection(&slave->mConn); - mSlaveList.append(slave); - connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone())); - connect(slave, SIGNAL(statusUpdate(IdleSlave*)), - this, SLOT(slotSlaveStatus(IdleSlave*))); - if (!mTimer.isActive()) - { - mTimer.start(1000*10); + const int equalindex = sessionmanager.indexOf('='); + kDebug() << "SESSION_MANAGER" << sessionmanager; + if (equalindex > 0) { + const QByteArray sessionmanagervalue = sessionmanager.mid(equalindex + 1, sessionmanager.size() - equalindex - 1); + kDebug() << "SESSION_MANAGER" << sessionmanager << sessionmanagervalue; + m_adaptor->setLaunchEnv(QString::fromLatin1("SESSION_MANAGER"), sessionmanagervalue); } } -void -KLauncher::slotSlaveStatus(IdleSlave *slave) +int main(int argc, char *argv[]) { - QMutableListIterator it(mSlaveWaitRequest); - while(it.hasNext()) - { - SlaveWaitRequest *waitRequest = it.next(); - if (waitRequest->pid == slave->pid()) - { - QDBusConnection::sessionBus().send(waitRequest->transaction.createReply()); - it.remove(); - delete waitRequest; - } + KAboutData aboutData( + "klauncher", "kdelibs4", ki18n("KDE launcher"), + KDE_VERSION_STRING, + ki18n("KDE launcher - launches and autostarts applications") + ); + + KCmdLineArgs::init(argc, argv, &aboutData); + + const QByteArray sessionmanager = qgetenv("SESSION_MANAGER"); + + // NOTE: disables session manager entirely, for reference: + // https://www.x.org/releases/X11R7.7/doc/libSM/xsmp.html + ::unsetenv("SESSION_MANAGER"); + + KApplication app; + app.setQuitOnLastWindowClosed(false); + app.disableSessionManagement(); + app.quitOnSignal(); + + QDBusConnection session = QDBusConnection::sessionBus(); + if (!session.isConnected()) { + kWarning() << "No DBUS session-bus found. Check if you have started the DBUS server."; + return 1; } -} - -void -KLauncher::slotSlaveGone() -{ - IdleSlave *slave = (IdleSlave *) sender(); - mSlaveList.removeAll(slave); - if ((mSlaveList.count() == 0) && (mTimer.isActive())) - { - mTimer.stop(); + QDBusReply sessionReply = session.interface()->isServiceRegistered("org.kde.klauncher"); + if (sessionReply.isValid() && sessionReply.value() == true) { + kWarning() << "Another instance of klauncher is already running!"; + return 2; } -} -void -KLauncher::idleTimeout() -{ - const time_t now = time(0); - foreach (IdleSlave *slave, mSlaveList) - { - if (slave->age(now) > SLAVE_MAX_IDLE) - { - // killing idle slave - delete slave; - } - } -} + KLauncher klauncher(&app); + klauncher.setSessionManager(sessionmanager); -void KLauncher::reparseConfiguration() -{ - KProtocolManager::reparseConfiguration(); - foreach (IdleSlave *slave, mSlaveList) - slave->reparseConfiguration(); -} - -void KLauncher::terminate_kdeinit() -{ - kDebug(7016); - klauncher_header request_header; - request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; - request_header.arg_length = 0; - kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); + return app.exec(); // keep running } #include "moc_klauncher.cpp" diff --git a/kinit/klauncher.h b/kinit/klauncher.h index 270ce2bf..2299d11a 100644 --- a/kinit/klauncher.h +++ b/kinit/klauncher.h @@ -1,251 +1,39 @@ -/* - This file is part of the KDE libraries - Copyright (c) 1999 Waldo Bastian +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ -#ifndef _KLAUNCHER_H_ -#define _KLAUNCHER_H_ +#ifndef KLAUNCHER_H +#define KLAUNCHER_H -#include "autostart.h" +#include "klauncher_adaptor.h" -#include -#include -#include - -#ifdef Q_WS_X11 -#include -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include - -class IdleSlave : public QObject -{ - Q_OBJECT -public: - explicit IdleSlave(QObject *parent); - bool match( const QString &protocol, const QString &host, bool connected) const; - void connect( const QString &app_socket); - pid_t pid() const { return mPid;} - int age(time_t now) const; - void reparseConfiguration(); - bool onHold(const KUrl &url) const; - QString protocol() const {return mProtocol;} - -Q_SIGNALS: - void statusUpdate(IdleSlave *); - -protected Q_SLOTS: - void gotInput(); - -public: - KIO::Connection mConn; -protected: - QString mProtocol; - QString mHost; - bool mConnected; - pid_t mPid; - time_t mBirthDate; - bool mOnHold; - KUrl mUrl; -}; - -class SlaveWaitRequest -{ -public: - pid_t pid; - QDBusMessage transaction; -}; - -class KLaunchRequest -{ -public: - QString name; - QStringList arg_list; - QString dbus_name; - QString tolerant_dbus_name; - enum status_t { Init = 0, Launching, Running, Error, Done }; - pid_t pid; - status_t status; - QDBusMessage transaction; - KService::DBusStartupType dbus_startup_type; - bool autoStart; - QString errorMsg; -#ifdef Q_WS_X11 - QByteArray startup_id; // "" is the default, "0" for none - QByteArray startup_dpy; // Display to send startup notification to. -#endif - QStringList envs; // env. variables to be app's environment - QString cwd; -}; - -struct serviceResult -{ - int result; // 0 means success. > 0 means error (-1 means pending) - QString dbusName; // Contains DBUS name on success - QString error; // Contains error description on failure. - pid_t pid; -}; +#include class KLauncher : public QObject { - Q_OBJECT - + Q_OBJECT public: - KLauncher(int kdeinitSocket); + KLauncher(QObject *parent = nullptr); + ~KLauncher(); - ~KLauncher(); + void setSessionManager(const QByteArray &sessionmanager); - void close(); - -public slots: - void destruct(); // exit! - -protected: - void processDied(pid_t pid, long exitStatus); - - void requestStart(KLaunchRequest *request); - void requestDone(KLaunchRequest *request); - - bool start_service(KService::Ptr service, const QStringList &urls, - const QStringList &envs, const QByteArray &startup_id, - bool blind, bool autoStart, QDBusMessage msg ); - - void createArgs( KLaunchRequest *request, const KService::Ptr service, - const QStringList &url); - - void queueRequest(KLaunchRequest *); - - void send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray &startup_id, - const QStringList &envs ); - void cancel_service_startup_info( KLaunchRequest *request, const QByteArray& startup_id, - const QStringList &envs ); - -Q_SIGNALS: - void autoStart0Done(); - void autoStart1Done(); - void autoStart2Done(); - -public: // remote methods, called by KLauncherAdaptor - void autoStart(int phase = 1); - - /** - * Starts a program. - */ - void exec_blind(const QString &name, const QStringList &arg_list); - - bool kdeinit_exec(const QString &app, const QStringList &args, - const QString& workdir, const QStringList &envs, - const QString &startup_id, bool wait, QDBusMessage msg); - - void reparseConfiguration(); - void setLaunchEnv(const QString &name, const QString &value); - - /** - * Start a service by desktop name. - * - * 'serviceName' refers to a desktop file describing the service. - * The service is looked up anywhere in $KDEDIR/applnk and/or - * $KDEDIR/services. - * E.g. it should have the form "korganizer". - * - * 'url', if not empty, will be passed to the service as - * argument. - * - * 'envs' are environment variables that will be added - * to this program's environment before starting it - * - * 'startup_id' is for application startup notification, - * "" is the default, "0" for none - */ - bool start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg); - - /** - * Start a service by desktop path. - * - * 'serviceName' refers to a desktop file describing the service. - * This may be an absolute path or a path relative to $KDEDIRS/applnk - * and/or $KDEDIRS/services - * E.g. it should have the form "Applications/korganizer.desktop" or - * "/opt/kde/share/applnk/Applications/korganizer.desktop". - * Note that for absolute paths the restrictions of - * KDesktopFile::isAuthorizedDesktopFile() are obeyed for security. - * - * 'url', if not empty, will be passed to the service as - * argument. - * - * 'envs' are environment variables that will be added - * to this program's environment before starting it - * - * 'startup_id' is for application startup notification, - * "" is the default, "0" for none - */ - bool start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg); - - pid_t requestHoldSlave(const KUrl &url, const QString &app_socket); - - pid_t requestSlave(const QString &protocol, const QString &host, - const QString &app_socket, QString &error); - /** - * Return true of there is a slave held for @p url. - * @since 4.7 - */ - bool checkForHeldSlave(const QString &url); - void waitForSlave(pid_t pid, QDBusMessage msg); - void terminate_kdeinit(); - -public Q_SLOTS: - void slotAutoStart(); - void slotDequeue(); - void slotKDEInitData(int); - void slotNameOwnerChanged(const QString &name, const QString &oldOnwer, const QString &newOwner); - void slotSlaveStatus(IdleSlave *); - void acceptSlave(); - void slotSlaveGone(); - void idleTimeout(); - -public: - serviceResult requestResult; // accessed by the adaptor -protected: - QList requestList; // Requests being handled - QList requestQueue; // Requests waiting to being handled - KLaunchRequest *lastRequest; - QList mSlaveWaitRequest; - int kdeinitSocket; - QSocketNotifier *kdeinitNotifier; - KIO::ConnectionServer mConnectionServer; - QList mSlaveList; - QTimer mTimer; - QTimer mAutoTimer; - bool bProcessingQueue; - AutoStart mAutoStart; - QString mSlaveDebug; - QString mSlaveValgrind; - QString mSlaveValgrindSkin; -#ifdef Q_WS_X11 - Display *mCached_dpy; -#endif - void processRequestReturn(int status, const QByteArray &requestData); +private: + KLauncherAdaptor* m_adaptor; }; -#endif + +#endif // KLAUNCHER_H diff --git a/kinit/klauncher_adaptor.cpp b/kinit/klauncher_adaptor.cpp index 9517d8eb..3a4dc070 100644 --- a/kinit/klauncher_adaptor.cpp +++ b/kinit/klauncher_adaptor.cpp @@ -1,141 +1,351 @@ -/* - * Copyright 2006, 2007 Thiago Macieira - * Copyright 2006-2008 David Faure - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) version 3, or any - * later version accepted by the membership of KDE e.V. (or its - * successor approved by the membership of KDE e.V.), which shall - * act as a proxy defined in Section 6 of version 3 of the license. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - */ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ #include "klauncher_adaptor.h" -#include "klauncher.h" +#include "kprotocolmanager.h" +#include "krun.h" +#include "kstandarddirs.h" +#include "kautostart.h" +#include "kdebug.h" -/* - * Implementation of adaptor class KLauncherAdaptor - * - * This file was initially generated by dbusidl2cpp version 0.4, - * but then modified by hand. - */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const int s_eventstime = 250; +static const int s_sleeptime = 50; +static const qint64 s_servicetimeout = 10000; // 10sec KLauncherAdaptor::KLauncherAdaptor(QObject *parent) - : QDBusAbstractAdaptor(parent) + : QDBusAbstractAdaptor(parent) { - // constructor - setAutoRelaySignals(true); + m_environment = QProcessEnvironment::systemEnvironment(); } KLauncherAdaptor::~KLauncherAdaptor() { - // destructor + kDebug() << "terminating processes" << m_processes.size(); + while (!m_processes.isEmpty()) { + QProcess* process = m_processes.takeLast(); + disconnect(process, 0, this, 0); + process->terminate(); + process->waitForFinished(); + } } void KLauncherAdaptor::autoStart(int phase) { - // handle method call org.kde.KLauncher.autoStart - static_cast(parent())->autoStart(phase); + if (m_autostart.isEmpty()) { + m_autostart = KGlobal::dirs()->findAllResources( + "autostart", + QString::fromLatin1("*.desktop"), + KStandardDirs::NoDuplicates + ); + } + + kDebug() << "autostart, phase" << phase; + foreach(const QString &it, m_autostart) { + KAutostart kautostart(it); + if (!kautostart.autostarts(QString::fromLatin1("KDE"), KAutostart::CheckAll)) { + continue; + } + if (kautostart.startPhase() != phase) { + continue; + } + KService kservice(it); + QStringList programandargs = KRun::processDesktopExec(kservice, KUrl::List()); + if (programandargs.isEmpty()) { + kWarning() << "could not process service" << kservice.entryPath(); + continue; + } + const QString program = programandargs.takeFirst(); + const QStringList programargs = programandargs; + exec_blind(program, programargs); + } + switch (phase) { + case 0: { + emit autoStart0Done(); + break; + } + case 1: { + emit autoStart1Done(); + break; + } + case 2: { + emit autoStart2Done(); + break; + } + } } void KLauncherAdaptor::exec_blind(const QString &name, const QStringList &arg_list) { - // handle method call org.kde.KLauncher.exec_blind - static_cast(parent())->exec_blind(name, arg_list); + const QString appexe = findExe(name); + if (appexe.isEmpty()) { + kWarning() << "Could not find" << name; + return; + } + + kDebug() << "blind starting" << appexe << arg_list << m_environment.toStringList(); + const QString envexe = findExe("env"); + if (envexe.isEmpty()) { + kWarning() << "env program not found"; + QProcess::startDetached(appexe, arg_list); + return; + } + + QStringList envargs = m_environment.toStringList(); + envargs += appexe; + envargs += arg_list; + QProcess::startDetached(envexe, envargs); } -int KLauncherAdaptor::kdeinit_exec(const QString &app, const QStringList &args, const QStringList &env, const QString& startup_id, const QDBusMessage &msg, QString &, QString &, qint64 &) +int KLauncherAdaptor::kdeinit_exec(const QString &app, const QStringList &args, const QStringList &env, const QString& startup_id, + const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) { - // handle method call org.kde.KLauncher.kdeinit_exec - static_cast(parent())->kdeinit_exec(app, args, QString(), env, startup_id, false, msg); - return 0; // delayed reply + return kdeinit_exec_with_workdir(app, args, QDir::currentPath(), env, startup_id, msg, dbusServiceName, error, pid); } -int KLauncherAdaptor::kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &env, const QString& startup_id, const QDBusMessage &msg, QString &, QString &, qint64 &) +int KLauncherAdaptor::kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &env, const QString& startup_id, + const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) { - // handle method call org.kde.KLauncher.kdeinit_exec_wait - static_cast(parent())->kdeinit_exec(app, args, QString(), env, startup_id, true, msg); - return 0; // delayed reply + const int result = kdeinit_exec(app, args, env, startup_id, msg, dbusServiceName, error, pid); + if (result != KLauncherAdaptor::NoError) { + return result; + } + kDebug() << "waiting for" << pid; + while (::kill(pid, 0) >= 0) { + QApplication::processEvents(QEventLoop::AllEvents, s_eventstime); + QThread::msleep(s_sleeptime); + } + int pidstate = 0; + ::waitpid(pid, &pidstate, WNOHANG); + kDebug() << "done waiting for" << pid << ", exit status" << WEXITSTATUS(pidstate); + return WEXITSTATUS(pidstate); } -int KLauncherAdaptor::kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QString& workdir, const QStringList &env, const QString& startup_id, const QDBusMessage &msg, QString &, QString &, qint64 &) +int KLauncherAdaptor::kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QString& workdir, const QStringList &env, const QString& startup_id, + const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) { - // handle method call org.kde.KLauncher.kdeinit_exec_with_workdir - static_cast(parent())->kdeinit_exec(app, args, workdir, env, startup_id, false, msg); - return 0; // delayed reply + const QString appexe = findExe(app); + if (appexe.isEmpty()) { + error = i18n("Could not find: %1", app); + return KLauncherAdaptor::FindError; + } + + QProcess* process = new QProcess(this); + connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(slotProcessStateChanged(QProcess::ProcessState))); + connect(process, SIGNAL(finished(int)), this, SLOT(slotProcessFinished(int))); + m_processes.append(process); + QProcessEnvironment processenv = m_environment; + foreach (const QString &it, env) { + const int equalindex = it.indexOf(QLatin1Char('=')); + if (equalindex <= 0) { + kWarning() << "Invalid environment variable" << it; + continue; + } + const QString environmentvar = it.mid(0, equalindex); + const QString environmentvalue = it.mid(equalindex + 1, it.size() - equalindex - 1); + kDebug() << "adding to environment" << environmentvar << environmentvalue; + processenv.insert(environmentvar, environmentvalue); + } + process->setProcessEnvironment(processenv); + process->setWorkingDirectory(workdir); + kDebug() << "starting" << appexe << args << env << processenv.toStringList(); + m_kstartupinfoid = KStartupInfoId(); + m_kstartupinfoid.initId(startup_id.toLatin1()); + m_kstartupinfodata = KStartupInfoData(); + m_kstartupinfodata.setBin(appexe); + process->start(appexe, args); + sendSIStart(); + while (process->state() == QProcess::Starting) { + QApplication::processEvents(QEventLoop::AllEvents, s_eventstime); + QThread::msleep(s_sleeptime); + } + if (process->error() == QProcess::FailedToStart || process->error() == QProcess::Crashed) { + sendSIFinish(); + error = i18n("Could not start: %1", appexe); + return KLauncherAdaptor::ExecError; + } + if (dbusServiceName.isEmpty()) { + // if service name is not empty it will be send when the service is up + sendSIFinish(); + } + + error.clear(); + pid = process->pid(); + return KLauncherAdaptor::NoError; } void KLauncherAdaptor::reparseConfiguration() { - // handle method call org.kde.KLauncher.reparseConfiguration - static_cast(parent())->reparseConfiguration(); -} - -qint64 KLauncherAdaptor::requestHoldSlave(const QString &url, const QString &app_socket) -{ - // handle method call org.kde.KLauncher.requestHoldSlave - return static_cast(parent())->requestHoldSlave(KUrl(url), app_socket); -} - -qint64 KLauncherAdaptor::requestSlave(const QString &protocol, const QString &host, const QString &app_socket, QString &error) -{ - // handle method call org.kde.KLauncher.requestSlave - return static_cast(parent())->requestSlave(protocol, host, app_socket, error); -} - -bool KLauncherAdaptor::checkForHeldSlave (const QString &url) -{ - return static_cast(parent())->checkForHeldSlave(url); + kDebug() << "reparsing configuration"; + KProtocolManager::reparseConfiguration(); } void KLauncherAdaptor::setLaunchEnv(const QString &name, const QString &value) { - // handle method call org.kde.KLauncher.setLaunchEnv - static_cast(parent())->setLaunchEnv(name, value); + kDebug() << "setting environment variable" << name << "to" << value; + m_environment.insert(name, value); } -int KLauncherAdaptor::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) +int KLauncherAdaptor::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, + const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) { - // handle method call org.kde.KLauncher.start_service_by_desktop_name - KLauncher *p = static_cast(parent()); - p->start_service_by_desktop_name(serviceName, urls, envs, startup_id, blind, msg); - dbusServiceName = p->requestResult.dbusName; - error = p->requestResult.error; - pid = p->requestResult.pid; - return p->requestResult.result; + KService::Ptr kservice = KService::serviceByDesktopName(serviceName); + if (!kservice) { + error = i18n("Invalid service name: %1", serviceName); + return KLauncherAdaptor::ServiceError; + } + return start_service_by_desktop_path(kservice->entryPath(), urls, envs, startup_id, blind, msg, dbusServiceName, error, pid); } -int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) +int KLauncherAdaptor::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, + const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid) { - // handle method call org.kde.KLauncher.start_service_by_desktop_path - KLauncher *p = static_cast(parent()); - p->start_service_by_desktop_path(serviceName, urls, envs, startup_id, blind, msg); - dbusServiceName = p->requestResult.dbusName; - error = p->requestResult.error; - pid = p->requestResult.pid; - return p->requestResult.result; + KService::Ptr kservice = KService::serviceByStorageId(serviceName); + if (!kservice) { + error = i18n("Invalid service path: %1", serviceName); + return KLauncherAdaptor::ServiceError; + } + if (urls.size() > 1 && !kservice->allowMultipleFiles()) { + // TODO: start multiple instances for each URL + error = i18n("Service does not support multiple files: %1", serviceName); + return KLauncherAdaptor::ServiceError; + } + QStringList programandargs = KRun::processDesktopExec(*kservice, urls); + if (programandargs.isEmpty()) { + error = i18n("Could not process service: %1", kservice->entryPath()); + return KLauncherAdaptor::ArgumentsError; + } + const QString program = programandargs.takeFirst(); + const QStringList programargs = programandargs; + const KService::DBusStartupType dbusstartuptype = kservice->dbusStartupType(); + dbusServiceName = kservice->property(QString::fromLatin1("X-DBUS-ServiceName"), QVariant::String).toString(); + const int result = kdeinit_exec(program, programargs, envs, startup_id, msg, dbusServiceName, error, pid); + if (result != KLauncherAdaptor::NoError) { + // sendSIFinish() is called on exec error + return result; + } + // blind means do not wait, even if its type is wait + if (blind || dbusstartuptype == KService::DBusNone) { + sendSIFinish(); + return result; + } else if (dbusstartuptype != KService::DBusNone && dbusServiceName.isEmpty()) { + // not going to guess what service to wait for, bud + kWarning() << "X-DBUS-ServiceName not specified in" << kservice->entryPath(); + sendSIFinish(); + return result; + } else if (dbusstartuptype == KService::DBusMulti) { + dbusServiceName.append(QLatin1Char('-')); + dbusServiceName.append(QString::number(pid)); + } + kDebug() << "waiting for" << pid << dbusServiceName; + QDBusConnection session = QDBusConnection::sessionBus(); + QElapsedTimer elapsedtime; + elapsedtime.start(); + while (true) { + QDBusReply sessionreply = session.interface()->isServiceRegistered(dbusServiceName); + if (!sessionreply.isValid()) { + sendSIFinish(); + error = i18n("Invalid D-Bus reply for: %1", dbusServiceName); + return KLauncherAdaptor::DBusError; + } + // the service unregistered + if (sessionreply.value() == false && dbusstartuptype == KService::DBusWait) { + break; + } + // the service registered + if (sessionreply.value() == true && dbusstartuptype != KService::DBusWait) { + break; + } + // or the program is just not registering the service at all + if (elapsedtime.elapsed() >= s_servicetimeout && dbusstartuptype != KService::DBusWait) { + kWarning() << "timed out for" << pid << ", service name" << dbusServiceName; + break; + } + QApplication::processEvents(QEventLoop::AllEvents, s_eventstime); + QThread::msleep(s_sleeptime); + } + kDebug() << "done waiting for" << pid << ", service name" << dbusServiceName; + sendSIFinish(); + return result; } -void KLauncherAdaptor::waitForSlave(qint64 pid, const QDBusMessage &msg) +void KLauncherAdaptor::slotProcessStateChanged(QProcess::ProcessState state) { - // handle method call org.kde.KLauncher.waitForSlave - static_cast(parent())->waitForSlave(pid, msg); + QProcess* process = qobject_cast(sender()); + kDebug() << "process state changed" << process << state; + if (state == QProcess::Starting && !m_kstartupinfoid.none()) { + m_kstartupinfodata.addPid(process->pid()); + sendSIUpdate(); + } else if (state == QProcess::NotRunning && m_kstartupinfodata.is_pid(process->pid())) { + sendSIFinish(); + } } -void KLauncherAdaptor::terminate_kdeinit() +void KLauncherAdaptor::slotProcessFinished(int exitcode) { - KLauncher *p = static_cast(parent()); - p->terminate_kdeinit(); + QProcess* process = qobject_cast(sender()); + kDebug() << "process finished" << process << exitcode; + m_processes.removeAll(process); +} + +QString KLauncherAdaptor::findExe(const QString &app) const +{ + if (QDir::isAbsolutePath(app)) { + return app; + } + const QString environmentpath = m_environment.value(QString::fromLatin1("PATH"), QString()); + return KStandardDirs::findExe(app, environmentpath); +} + +void KLauncherAdaptor::sendSIStart() const +{ + kDebug() << "sending ASN start"; + KStartupInfo::sendStartup(m_kstartupinfoid, m_kstartupinfodata); +} + +void KLauncherAdaptor::sendSIUpdate() +{ + if (m_kstartupinfoid.none()) { + return; + } + kDebug() << "sending ASN update"; + KStartupInfo::sendChange(m_kstartupinfoid, m_kstartupinfodata); +} + +void KLauncherAdaptor::sendSIFinish() +{ + if (m_kstartupinfoid.none()) { + return; + } + kDebug() << "sending ASN finish"; + KStartupInfo::sendFinish(m_kstartupinfoid, m_kstartupinfodata); + m_kstartupinfoid = KStartupInfoId(); + m_kstartupinfodata = KStartupInfoData(); } #include "moc_klauncher_adaptor.cpp" diff --git a/kinit/klauncher_adaptor.h b/kinit/klauncher_adaptor.h index dc4e91f9..b12e0808 100644 --- a/kinit/klauncher_adaptor.h +++ b/kinit/klauncher_adaptor.h @@ -1,47 +1,47 @@ -/* - * Copyright 2006, 2007 Thiago Macieira - * Copyright 2006-2008 David Faure - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) version 3, or any - * later version accepted by the membership of KDE e.V. (or its - * successor approved by the membership of KDE e.V.), which shall - * act as a proxy defined in Section 6 of version 3 of the license. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - */ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev -#ifndef KLAUNCHER_ADAPTOR_H_18181148166088 -#define KLAUNCHER_ADAPTOR_H_18181148166088 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. -#include -#include -#include + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. -#include -#include -#include -#include + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ -/* - * Adaptor class for interface org.kde.KLauncher - */ +#ifndef KLAUNCHER_ADAPTOR_H +#define KLAUNCHER_ADAPTOR_H + +#include "kstartupinfo.h" + +#include +#include +#include + +// Adaptor class for interface org.kde.KLauncher class KLauncherAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KLauncher") public: + enum KLauncherError { + NoError = 0, + ServiceError = 1, + FindError = 2, + ArgumentsError = 3, + ExecError = 4, + DBusError = 5 + }; + KLauncherAdaptor(QObject *parent); - virtual ~KLauncherAdaptor(); + ~KLauncherAdaptor(); public: // PROPERTIES public Q_SLOTS: // METHODS @@ -51,18 +51,30 @@ public Q_SLOTS: // METHODS int kdeinit_exec_wait(const QString &app, const QStringList &args, const QStringList &env, const QString& startup_id, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid); int kdeinit_exec_with_workdir(const QString &app, const QStringList &args, const QString& workdir, const QStringList &env, const QString& startup_id, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid); void reparseConfiguration(); - qint64 requestHoldSlave(const QString &url, const QString &app_socket); - qint64 requestSlave(const QString &protocol, const QString &host, const QString &app_socket, QString &error); - bool checkForHeldSlave (const QString &url); void setLaunchEnv(const QString &name, const QString &value); int start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid); int start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, const QDBusMessage &msg, QString &dbusServiceName, QString &error, qint64 &pid); - void waitForSlave(qint64 pid, const QDBusMessage &msg); - void terminate_kdeinit(); + Q_SIGNALS: // SIGNALS void autoStart0Done(); void autoStart1Done(); void autoStart2Done(); + +private Q_SLOTS: + void slotProcessStateChanged(QProcess::ProcessState state); + void slotProcessFinished(int exitcode); + +private: + QString findExe(const QString &app) const; + void sendSIStart() const; + void sendSIUpdate(); + void sendSIFinish(); + + KStartupInfoId m_kstartupinfoid; + KStartupInfoData m_kstartupinfodata; + QProcessEnvironment m_environment; + QList m_processes; + QStringList m_autostart; }; -#endif +#endif // KLAUNCHER_ADAPTOR_H diff --git a/kinit/klauncher_cmds.cpp b/kinit/klauncher_cmds.cpp deleted file mode 100644 index d9b74f8e..00000000 --- a/kinit/klauncher_cmds.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2009 David Faure - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License or ( at - * your option ) version 3 or, at the discretion of KDE e.V. ( which shall - * act as a proxy as in section 14 of the GPLv3 ), any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "klauncher_cmds.h" - -const char* commandToString(int command) -{ - switch (command) { - case LAUNCHER_SETENV: - return "LAUNCHER_SETENV"; - case LAUNCHER_CHILD_DIED: - return "LAUNCHER_CHILD_DIED"; - case LAUNCHER_OK: - return "LAUNCHER_OK"; - case LAUNCHER_ERROR: - return "LAUNCHER_ERROR"; - case LAUNCHER_TERMINATE_KDEINIT: - return "LAUNCHER_TERMINATE_KDEINIT"; - case LAUNCHER_DEBUG_WAIT: - return "LAUNCHER_DEBUG_WAIT"; - case LAUNCHER_EXEC_ASN: - return "LAUNCHER_EXEC_ASN"; - case LAUNCHER_EXEC: - return "LAUNCHER_EXEC"; - default: - return "UNKNOWN COMMAND"; - } -} diff --git a/kinit/klauncher_cmds.h b/kinit/klauncher_cmds.h deleted file mode 100644 index c2dc6aa4..00000000 --- a/kinit/klauncher_cmds.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 1999 Waldo Bastian - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef _KLAUNCHER_CMDS_H_ -#define _KLAUNCHER_CMDS_H_ - -typedef struct -{ - long cmd; - long arg_length; -} klauncher_header; - -/* Launcher commands: */ - -/* - * LAUNCHER_SETENV - * - * Change environment of future processes launched via kdeinit. - * DON'T use this if you want to change environment only for one - * application you're going to start. - * - * char *env_name; - * char *env_value; - */ -#define LAUNCHER_SETENV 1 - -/* - * LAUNCHER_CHILD_DIED - * - * Notification A child of kdeinit died. - * - * long pid; - * long exit_code; - */ -#define LAUNCHER_CHILD_DIED 2 - -/* - * LAUNCHER_OK - * - * Notification Last process launched ok. - * - * long pid; - */ -#define LAUNCHER_OK 3 - -/* - * LAUNCHER_ERROR - * - * Notification Last process could not be launched. - * - * char *error msg (utf8) - */ -#define LAUNCHER_ERROR 4 - -/* - * LAUNCHER_TERMINATE_KDEINIT - * - * Suicide is painless - */ -#define LAUNCHER_TERMINATE_KDEINIT 5 - -/* - * LAUNCHER_DEBUG_WAIT - * - * Next process started will do a sleep(1000000) - * before calling main()/kdemain() - * - * (Used for debugging io-slaves) - */ -#define LAUNCHER_DEBUG_WAIT 6 - -/* - * LAUNCHER_EXEC_ASN - * - * Start a new process. The given environment variables will - * be added to its environment before starting it. - * Starts app-startup notification. - * - * long argc: number of arguments - * char *args: arguments, argument 0 is the program to start. - * long envc: number of environment vars - * char *envs: environment strings. - * int avoid_loops : avoid using the first path in $PATH where - * this process binary is found in order to avoid - * infinite loop by binary->kdeinit_wrapper link in $PATH - * char* startup_id: app startup notification id, "0" for none, - * "" ( empty string ) is the default - * - */ -#define LAUNCHER_EXEC_ASN 7 - -/* - * LAUNCHER_EXEC - * - * Start a new process. The given environment variables will be - * added to its environment before starting it. - * There will be no app startup notification. - * - * long argc: number of arguments - * char *args: arguments, argument 0 is the program to start. - * long envc: number of environment vars - * char *envs: environment strings. - * int avoid_loops : avoid using the first path in $PATH where - * this process binary is found in order to avoid - * infinite loop by binary->kdeinit_wrapper link in $PATH - */ -#define LAUNCHER_EXEC 8 - -const char* commandToString(int command); - -#endif diff --git a/kinit/klauncher_main.cpp b/kinit/klauncher_main.cpp deleted file mode 100644 index a2a3f600..00000000 --- a/kinit/klauncher_main.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 1999 Waldo Bastian - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include - -#include -#include - -#include "klauncher.h" -#include -#include "kcrash.h" -#include "kdebug.h" -#include -#include -#include -#include -#include - -#include "klauncher_cmds.h" -#include - -static int sigpipe[ 2 ]; -static void sig_handler(int sig_num) -{ - // No recursion - KDE_signal( SIGHUP, SIG_IGN); - KDE_signal( SIGTERM, SIG_IGN); - fprintf(stderr, "klauncher: Exiting on signal %d\n", sig_num); - char tmp = 'x'; - write( sigpipe[ 1 ], &tmp, 1 ); -} - - -int main( int argc, char**argv ) -{ - // Started via kdeinit. - int launcherFd; - if (argc != 2 || memcmp(argv[1], "--fd=", 5) || !(launcherFd = atoi(argv[1] + 5))) - { - fprintf(stderr, "%s", i18n("klauncher: This program is not supposed to be started manually.\n" - "klauncher: It is started automatically by kdeinit4.\n").toLocal8Bit().data()); - return 1; - } - - - KComponentData componentData("klauncher", "kdelibs4"); - KGlobal::locale(); - - // WABA: Make sure not to enable session management. - putenv(strdup("SESSION_MANAGER=")); - - // We need a QCoreApplication to get a DBus event loop - QCoreApplication app(argc, argv); - app.setApplicationName( componentData.componentName() ); - - int maxTry = 3; - while(true) - { - QString service(QLatin1String("org.kde.klauncher")); // same as ktoolinvocation.cpp - if (!QDBusConnection::sessionBus().isConnected()) { - kWarning() << "No DBUS session-bus found. Check if you have started the DBUS server."; - return 1; - } - QDBusReply reply = - QDBusConnection::sessionBus().interface()->registerService(service); - if (!reply.isValid()) - { - kWarning() << "DBUS communication problem!"; - return 1; - } - if (reply == QDBusConnectionInterface::ServiceRegistered) - break; - - if (--maxTry == 0) - { - kWarning() << "Another instance of klauncher is already running!"; - return 1; - } - - // Wait a bit... - kWarning() << "Waiting for already running klauncher to exit."; - sleep(1); - - // Try again... - } - - KLauncher *launcher = new KLauncher(launcherFd); - QDBusConnection::sessionBus().registerObject(QString::fromLatin1("/"), launcher); - - if (pipe(sigpipe) != 0) { - perror("klauncher: pipe failed."); - return 1; - } - QSocketNotifier* signotif = new QSocketNotifier( sigpipe[ 0 ], QSocketNotifier::Read, launcher ); - QObject::connect( signotif, SIGNAL(activated(int)), launcher, SLOT(destruct())); - KCrash::setEmergencySaveFunction(sig_handler); - KDE_signal( SIGHUP, sig_handler); - KDE_signal( SIGPIPE, SIG_IGN); - KDE_signal( SIGTERM, sig_handler); - - return app.exec(); -} diff --git a/kinit/org.kde.klauncher.service.in b/kinit/org.kde.klauncher.service.in new file mode 100644 index 00000000..9e2f23a7 --- /dev/null +++ b/kinit/org.kde.klauncher.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.kde.klauncher +Exec=@KDE4_BIN_INSTALL_DIR@/klauncher4 diff --git a/kinit/proctitle.cpp b/kinit/proctitle.cpp deleted file mode 100644 index bd34feba..00000000 --- a/kinit/proctitle.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ProFTPD - FTP server daemon - * Copyright (c) 2007 The ProFTPD Project team //krazy:exclude=copyright - * Copyright (c) 2007 Alex Merry - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include "proctitle.h" -#include "config-kdeinit.h" - -#include -#include -#include -#include - -#ifdef HAVE_SETPROCTITLE -# define PF_ARGV_TYPE PF_ARGV_NONE -# include -# include -#endif /* HAVE_SETPROCTITLE */ - -#ifdef HAVE___PROGNAME -extern char *__progname; -#endif /* HAVE___PROGNAME */ -#ifdef HAVE___PROGNAME_FULL -extern char *__progname_full; -#endif /* HAVE___PROGNAME_FULL */ - -/** - * Set up the memory space for setting the proctitle - */ -void proctitle_init(int argc, char *argv[]) { -# ifdef HAVE___PROGNAME - /* Set the __progname variable so glibc and company - * don't go nuts. - */ - __progname = strdup("kdeinit4"); -# endif /* HAVE___PROGNAME */ -# ifdef HAVE___PROGNAME_FULL - /* __progname_full too */ - __progname_full = strdup(argv[0]); -# endif /* HAVE___PROGNAME_FULL */ -} - -void proctitle_set(const char *fmt, ...) { - va_list msg; - - if ( !fmt ) { - return; - } - - va_start(msg, fmt); - -#ifdef HAVE_SETPROCTITLE - char statbuf[BUFSIZ]; - memset(statbuf, 0, sizeof(statbuf)); - -# if __FreeBSD__ >= 4 && !defined(FREEBSD4_0) && !defined(FREEBSD4_1) - /* FreeBSD's setproctitle() automatically prepends the process name. */ - vsnprintf(statbuf, sizeof(statbuf), fmt, msg); - -# else /* FREEBSD4 */ - /* Manually append the process name for non-FreeBSD platforms. */ - snprintf(statbuf, sizeof(statbuf), "%s", "kdeinit4: "); - vsnprintf(statbuf + strlen(statbuf), - sizeof(statbuf) - strlen(statbuf), - fmt, - msg); - -# endif /* FREEBSD4 */ - setproctitle("%s", statbuf); -#endif /* HAVE_SETPROCTITLE */ - - va_end(msg); -} diff --git a/kinit/proctitle.h b/kinit/proctitle.h deleted file mode 100644 index bcb63f8e..00000000 --- a/kinit/proctitle.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ProFTPD - FTP server daemon - * Copyright (c) 2007 The ProFTPD Project team //krazy:exclude=copyright - * Copyright (c) 2007 Alex Merry - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifndef PROCTITLE_H -#define PROCTITLE_H - -/** - * Initialises the program data variables to allow the - * changing of the process title. This _must_ be called - * before proctitle_set, and must only be called once. - * - * @param argc argc, as passed to main() - * @param argv argv, as passed to main() - */ -void proctitle_init(int argc, char *argv[]); - -/** - * Change the process title. It accepts a variable number - * of arguments (a va_list) in the manner of the printf - * family of functions. See the documentation for - * printf for a description of the format string. - */ -void proctitle_set(const char *fmt, ...) -#if !defined(__INSURE__) - __attribute__ (( format ( printf, 1, 2 ) ) ) -#endif -; - -#endif /* PROCTITLE_H */ diff --git a/kio/kio/slave.cpp b/kio/kio/slave.cpp index a144b368..86cce3a3 100644 --- a/kio/kio/slave.cpp +++ b/kio/kio/slave.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include "kservice.h" #include @@ -300,10 +299,6 @@ void Slave::hold(const KUrl &url) emit slaveDied(this); } deref(); - // Call KLauncher::waitForSlave(pid); - { - KToolInvocation::klauncher()->waitForSlave(d->m_pid); - } } void Slave::suspend() @@ -402,95 +397,41 @@ Slave* Slave::createSlave( const QString &protocol, const KUrl& url, int& error, Slave *slave = new Slave(protocol); QString slaveAddress = slave->d_func()->slaveconnserver->address(); -#ifdef Q_OS_UNIX - // In such case we start the slave via QProcess. - // It's possible to force this by setting the env. variable - // KDE_FORK_SLAVES, Clearcase seems to require this. - static bool bForkSlaves = !qgetenv("KDE_FORK_SLAVES").isEmpty(); - - if (!bForkSlaves) + QString _name = KProtocolInfo::exec(protocol); + if (_name.isEmpty()) { - // check the UID of klauncher - QDBusReply reply = QDBusConnection::sessionBus().interface()->serviceUid(KToolInvocation::klauncher()->service()); - if (reply.isValid() && getuid() != reply) - bForkSlaves = true; - } - - if (bForkSlaves) - { - QString _name = KProtocolInfo::exec(protocol); - if (_name.isEmpty()) - { - error_text = i18n("Unknown protocol '%1'.", protocol); - error = KIO::ERR_CANNOT_LAUNCH_PROCESS; - delete slave; - return 0; - } - KPluginLoader lib(_name, KGlobal::mainComponent()); - QString lib_path = lib.fileName(); - if (lib_path.isEmpty()) - { - error_text = i18n("Can not find io-slave for protocol '%1'.", protocol); - error = KIO::ERR_CANNOT_LAUNCH_PROCESS; - delete slave; - return 0; - } - - const QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress; - kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress; - - QProcess::startDetached( KStandardDirs::findExe("kioslave"), args ); - - return slave; - } -#endif - - org::kde::KLauncher* klauncher = KToolInvocation::klauncher(); - QString errorStr; - QDBusReply reply = klauncher->requestSlave(protocol, url.host(), slaveAddress, errorStr); - if (!reply.isValid()) { - error_text = i18n("Cannot talk to klauncher: %1", klauncher->lastError().message() ); + error_text = i18n("Unknown protocol '%1'.", protocol); error = KIO::ERR_CANNOT_LAUNCH_PROCESS; delete slave; return 0; } - pid_t pid = reply; - if (!pid) + KPluginLoader lib(_name, KGlobal::mainComponent()); + QString lib_path = lib.fileName(); + if (lib_path.isEmpty()) { - error_text = i18n("Unable to create io-slave:\nklauncher said: %1", errorStr); + error_text = i18n("Can not find io-slave for protocol '%1'.", protocol); error = KIO::ERR_CANNOT_LAUNCH_PROCESS; delete slave; return 0; } - slave->setPID(pid); - QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); + + const QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress; + kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress; + + QProcess::startDetached( KStandardDirs::findExe("kioslave"), args ); + return slave; } Slave* Slave::holdSlave( const QString &protocol, const KUrl& url ) { //kDebug(7002) << "holdSlave" << protocol << "for" << url; - Slave *slave = new Slave(protocol); - QString slaveAddress = slave->d_func()->slaveconnserver->address(); - QDBusReply reply = KToolInvocation::klauncher()->requestHoldSlave(url.url(), slaveAddress); - if (!reply.isValid()) { - delete slave; - return 0; - } - pid_t pid = reply; - if (!pid) - { - delete slave; - return 0; - } - slave->setPID(pid); - QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); - return slave; + return 0; } bool Slave::checkForHeldSlave(const KUrl &url) { - return KToolInvocation::klauncher()->checkForHeldSlave(url.url()); + return false; } #include "moc_slave.cpp" diff --git a/kio/tests/krununittest.cpp b/kio/tests/krununittest.cpp index 44a1f764..e7eaff99 100644 --- a/kio/tests/krununittest.cpp +++ b/kio/tests/krununittest.cpp @@ -155,9 +155,9 @@ void KRunUnitTest::testProcessDesktopExecNoFile_data() KUrl::List l4; l4 << KUrl( "http://login:password@www.kde.org" ); // A real-world use case would be kate. - // But I picked kdeinit4 since it's installed by kdelibs - QString kdeinit = KStandardDirs::findExe("kdeinit4"); - if (kdeinit.isEmpty()) kdeinit = "kdeinit4"; + // But I picked klauncher4 since it's installed by kdelibs + QString klauncher = KStandardDirs::findExe("klauncher4"); + if (klauncher.isEmpty()) klauncher = "klauncher4"; QString kioexec = KStandardDirs::findExe("kioexec"); if (kioexec.isEmpty()) @@ -165,30 +165,30 @@ void KRunUnitTest::testProcessDesktopExecNoFile_data() QString kmailservice = KStandardDirs::findExe("kmailservice"); if (kmailservice.isEmpty()) kmailservice = "kmailservice"; - if (!kdeinit.isEmpty()) { + if (!klauncher.isEmpty()) { QVERIFY(!kmailservice.isEmpty()); } // NOTE: using QString() for concats to avoid QStringBuilder metatype, which is not valid - QTest::newRow("%U l0") << "kdeinit4 %U" << l0 << false << kdeinit; - QTest::newRow("%U l1") << "kdeinit4 %U" << l1 << false << QString(kdeinit + " /tmp"); - QTest::newRow("%U l2") << "kdeinit4 %U" << l2 << false << QString(kdeinit + " http://localhost/foo"); - QTest::newRow("%U l3") << "kdeinit4 %U" << l3 << false << QString(kdeinit + " /local/file http://remotehost.org/bar"); + QTest::newRow("%U l0") << "klauncher4 %U" << l0 << false << klauncher; + QTest::newRow("%U l1") << "klauncher4 %U" << l1 << false << QString(klauncher + " /tmp"); + QTest::newRow("%U l2") << "klauncher4 %U" << l2 << false << QString(klauncher + " http://localhost/foo"); + QTest::newRow("%U l3") << "klauncher4 %U" << l3 << false << QString(klauncher + " /local/file http://remotehost.org/bar"); - //QTest::newRow("%u l0") << "kdeinit4 %u" << l0 << false << kdeinit; // gives runtime warning - QTest::newRow("%u l1") << "kdeinit4 %u" << l1 << false << QString(kdeinit + " /tmp"); - QTest::newRow("%u l2") << "kdeinit4 %u" << l2 << false << QString(kdeinit + " http://localhost/foo"); - //QTest::newRow("%u l3") << "kdeinit4 %u" << l3 << false << kdeinit; // gives runtime warning + //QTest::newRow("%u l0") << "klauncher4 %u" << l0 << false << klauncher; // gives runtime warning + QTest::newRow("%u l1") << "klauncher4 %u" << l1 << false << QString(klauncher + " /tmp"); + QTest::newRow("%u l2") << "klauncher4 %u" << l2 << false << QString(klauncher + " http://localhost/foo"); + //QTest::newRow("%u l3") << "klauncher4 %u" << l3 << false << klauncher; // gives runtime warning - QTest::newRow("%F l0") << "kdeinit4 %F" << l0 << false << kdeinit; - QTest::newRow("%F l1") << "kdeinit4 %F" << l1 << false << QString(kdeinit + " /tmp"); - QTest::newRow("%F l2") << "kdeinit4 %F" << l2 << false << QString(kioexec + " 'kdeinit4 %F' http://localhost/foo"); - QTest::newRow("%F l3") << "kdeinit4 %F" << l3 << false << QString(kioexec + " 'kdeinit4 %F' file:///local/file http://remotehost.org/bar"); + QTest::newRow("%F l0") << "klauncher4 %F" << l0 << false << klauncher; + QTest::newRow("%F l1") << "klauncher4 %F" << l1 << false << QString(klauncher + " /tmp"); + QTest::newRow("%F l2") << "klauncher4 %F" << l2 << false << QString(kioexec + " 'klauncher4 %F' http://localhost/foo"); + QTest::newRow("%F l3") << "klauncher4 %F" << l3 << false << QString(kioexec + " 'klauncher4 %F' file:///local/file http://remotehost.org/bar"); - QTest::newRow("%F l1 tempfile") << "kdeinit4 %F" << l1 << true << QString(kioexec + " --tempfiles 'kdeinit4 %F' file:///tmp"); + QTest::newRow("%F l1 tempfile") << "klauncher4 %F" << l1 << true << QString(kioexec + " --tempfiles 'klauncher4 %F' file:///tmp"); - QTest::newRow("sh -c kdeinit4 %F") << "sh -c \"kdeinit4 \"'\\\"'\"%F\"'\\\"'" - << l1 << false << QString(m_sh + " -c 'kdeinit4 \\\"/tmp\\\"'"); + QTest::newRow("sh -c klauncher4 %F") << "sh -c \"klauncher4 \"'\\\"'\"%F\"'\\\"'" + << l1 << false << QString(m_sh + " -c 'klauncher4 \\\"/tmp\\\"'"); QTest::newRow("kmailservice %u l1") << "kmailservice %u" << l1 << false << QString(kmailservice + " /tmp"); QTest::newRow("kmailservice %u l4") << "kmailservice %u" << l4 << false << QString(kmailservice + " http://login:password@www.kde.org");