File indexing completed on 2019-04-16 12:09:26

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