File indexing completed on 2024-04-14 14:23:51
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include <config-kdeinit.h> 0009 0010 #include <klauncher_debug.h> 0011 0012 #include <qplatformdefs.h> 0013 0014 #include "klauncher.h" 0015 #include <KCrash> 0016 #include <stdio.h> 0017 #include <stdlib.h> 0018 #include <signal.h> 0019 #include <KLocalizedString> 0020 0021 #include "klauncher_cmds.h" 0022 #include <QGuiApplication> 0023 #include <QFile> 0024 #include <QDebug> 0025 #include <QDBusConnectionInterface> 0026 #include <QThread> 0027 0028 #ifdef Q_OS_OSX 0029 #include <CoreFoundation/CoreFoundation.h> 0030 #endif 0031 0032 #ifndef USE_KPROCESS_FOR_KIOSLAVES 0033 static int sigpipe[ 2 ]; 0034 static void sig_handler(int sig_num) 0035 { 0036 // No recursion 0037 signal(SIGHUP, SIG_IGN); 0038 signal(SIGTERM, SIG_IGN); 0039 fprintf(stderr, "klauncher: Exiting on signal %d\n", sig_num); 0040 char tmp = 'x'; 0041 write(sigpipe[ 1 ], &tmp, 1); 0042 } 0043 #endif 0044 0045 #if defined(Q_OS_DARWIN) 0046 // Copied from kkernel_mac.cpp 0047 bool dbus_initialized = false; 0048 0049 /** 0050 Set the D-Bus environment based on session bus socket 0051 */ 0052 0053 bool mac_set_dbus_address(QString value) 0054 { 0055 if (!value.isEmpty() && QFile::exists(value) && (QFile::permissions(value) & QFile::WriteUser)) { 0056 value = QLatin1String("unix:path=") + value; 0057 qputenv("DBUS_SESSION_BUS_ADDRESS", value.toLocal8Bit()); 0058 // qDebug() << "set session bus address to" << value; 0059 return true; 0060 } 0061 return false; 0062 } 0063 0064 /** 0065 Make sure D-Bus is initialized, by any means necessary. 0066 */ 0067 0068 void mac_initialize_dbus() 0069 { 0070 enum 0071 { 0072 timeout = 3000 // msec. Copied from old kdecore/kernel/kkernel_mac.cpp 0073 }; 0074 0075 if (dbus_initialized) { 0076 return; 0077 } 0078 0079 QString dbusVar = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS")); 0080 if (!dbusVar.isEmpty()) { 0081 dbus_initialized = true; 0082 return; 0083 } 0084 0085 dbusVar = QFile::decodeName(qgetenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")); 0086 if (mac_set_dbus_address(dbusVar)) { 0087 dbus_initialized = true; 0088 return; 0089 } 0090 0091 QString externalProc; 0092 QStringList path = QFile::decodeName(qgetenv("KDEDIRS")).split(QLatin1Char(':')).replaceInStrings(QRegExp(QLatin1String("$")), QLatin1String("/bin")); 0093 path << QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':')) << QLatin1String("/usr/local/bin"); 0094 0095 for (int i = 0; i < path.size(); ++i) { 0096 QString testLaunchctl = QString(path.at(i)).append(QLatin1String("/launchctl")); 0097 if (QFile(testLaunchctl).exists()) { 0098 externalProc = testLaunchctl; 0099 break; 0100 } 0101 } 0102 0103 if (!externalProc.isEmpty()) { 0104 QProcess qp; 0105 qp.setTextModeEnabled(true); 0106 0107 qp.start(externalProc, QStringList() << QLatin1String("getenv") << QLatin1String("DBUS_LAUNCHD_SESSION_BUS_SOCKET")); 0108 if (!qp.waitForFinished(timeout)) { 0109 // qDebug() << "error running" << externalProc << qp.errorString(); 0110 return; 0111 } 0112 if (qp.exitCode() != 0) { 0113 // qDebug() << externalProc << "unsuccessful:" << qp.readAllStandardError(); 0114 return; 0115 } 0116 0117 QString line = QString::fromLatin1(qp.readLine()).trimmed(); // read the first line 0118 if (mac_set_dbus_address(line)) { 0119 dbus_initialized = true; // hooray 0120 } 0121 } 0122 0123 if (dbus_initialized == false) { 0124 // qDebug() << "warning: unable to initialize D-Bus environment!"; 0125 } 0126 0127 } 0128 #endif 0129 0130 extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) 0131 { 0132 #ifndef Q_OS_WIN 0133 // Started via kdeinit. 0134 int launcherFd; 0135 if (argc != 2 || memcmp(argv[1], "--fd=", 5) || !(launcherFd = atoi(argv[1] + 5))) { 0136 fprintf(stderr, "%s", i18n("klauncher: This program is not supposed to be started manually.\n" 0137 "klauncher: It is started automatically by kdeinit5.\n").toLocal8Bit().data()); 0138 return 1; 0139 } 0140 #endif 0141 0142 #if defined(Q_OS_DARWIN) 0143 CFBundleRef mainBundle = CFBundleGetMainBundle(); 0144 if (mainBundle) { 0145 // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist, 0146 // for regular executables it is obtained in another way. 0147 CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(mainBundle); 0148 if (infoDict) { 0149 // Add or set the "LSUIElement" key with/to value "1". This can simply be a CFString. 0150 CFDictionarySetValue(infoDict, CFSTR("LSUIElement"), CFSTR("1")); 0151 // That's it. We're now considered as an "agent" by the window server, and thus will have 0152 // neither menubar nor presence in the Dock or App Switcher. 0153 } 0154 } 0155 mac_initialize_dbus(); 0156 #endif 0157 0158 // WABA: Make sure not to enable session management. 0159 qunsetenv("SESSION_MANAGER"); 0160 0161 // Disable the GLib event loop (rh#983110) 0162 const bool wasQtNoGlibSet = !qEnvironmentVariableIsEmpty("QT_NO_GLIB"); 0163 if (!wasQtNoGlibSet) { 0164 qputenv("QT_NO_GLIB", "1"); 0165 } 0166 0167 // We need a QGuiApplication as we use X11 0168 QGuiApplication app(argc, argv); 0169 app.setApplicationName(QStringLiteral("klauncher")); 0170 0171 // Now get rid of QT_NO_GLIB again so launched processes don't inherit it 0172 if (!wasQtNoGlibSet) { 0173 qunsetenv("QT_NO_GLIB"); 0174 } 0175 0176 int maxTry = 3; 0177 while (true) { 0178 QString service(QStringLiteral("org.kde.klauncher5")); // same as ktoolinvocation.cpp 0179 if (!QDBusConnection::sessionBus().isConnected()) { 0180 qCWarning(KLAUNCHER) << "No D-Bus session-bus found. Check if you have started the D-Bus server."; 0181 return 1; 0182 } 0183 QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply = 0184 QDBusConnection::sessionBus().interface()->registerService(service); 0185 if (!reply.isValid()) { 0186 qCWarning(KLAUNCHER) << "D-Bus communication problem!"; 0187 return 1; 0188 } 0189 if (reply == QDBusConnectionInterface::ServiceRegistered) { 0190 break; 0191 } 0192 0193 if (--maxTry == 0) { 0194 qCWarning(KLAUNCHER) << "Another instance of klauncher is already running!"; 0195 return 1; 0196 } 0197 0198 // Wait a bit... 0199 qCWarning(KLAUNCHER) << "Waiting for already running klauncher to exit."; 0200 QThread::sleep(1); 0201 0202 // Try again... 0203 } 0204 0205 #ifndef USE_KPROCESS_FOR_KIOSLAVES 0206 KLauncher *launcher = new KLauncher(launcherFd); 0207 #else 0208 KLauncher *launcher = new KLauncher(); 0209 #endif 0210 QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), launcher); 0211 0212 #ifndef USE_KPROCESS_FOR_KIOSLAVES 0213 if (pipe(sigpipe) != 0) { 0214 perror("klauncher: pipe failed."); 0215 return 1; 0216 } 0217 QSocketNotifier *signotif = new QSocketNotifier(sigpipe[ 0 ], QSocketNotifier::Read, launcher); 0218 QObject::connect(signotif, SIGNAL(activated(int)), launcher, SLOT(destruct())); 0219 KCrash::setEmergencySaveFunction(sig_handler); 0220 signal(SIGHUP, sig_handler); 0221 signal(SIGPIPE, SIG_IGN); 0222 signal(SIGTERM, sig_handler); 0223 #endif 0224 0225 return app.exec(); 0226 }