File indexing completed on 2024-04-21 14:55:35

0001 /*
0002    This file is part of the KDE libraries
0003    Copyright (C) 2008 Benjamin Reed <rangerrick@befunk.com>
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 "kkernel_mac.h"
0021 
0022 #ifdef Q_OS_MACX
0023 
0024 #include <unistd.h>
0025 #include <string.h>
0026 #include <stdio.h>
0027 #include <stdlib.h>
0028 #include <sys/param.h>
0029 #include <crt_externs.h>
0030 #include <mach-o/dyld.h>
0031 
0032 #include <CoreFoundation/CFBundle.h>
0033 #include <CoreFoundation/CFURL.h>
0034 #include <QFile>
0035 #include <QProcess>
0036 #include <QStringList>
0037 #include <qvarlengtharray.h>
0038 
0039 #include <ksharedconfig.h>
0040 #include <kconfig.h>
0041 #include <kdebug.h>
0042 
0043 int timeout = 3000; // msec
0044 
0045 bool dbus_initialized = false;
0046 
0047 /**
0048   qAppFileName() is not public in qt4/mac, so we need to redo it here
0049 */
0050 
0051 QString convert_CFString_to_QString(CFStringRef str)
0052 {
0053     CFIndex length = CFStringGetLength(str);
0054     const UniChar *chars = CFStringGetCharactersPtr(str);
0055     if (chars) {
0056         return QString(reinterpret_cast<const QChar *>(chars), length);
0057     }
0058 
0059     QVarLengthArray<UniChar> buffer(length);
0060     CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
0061     return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
0062 }
0063 
0064 /**
0065  Calling CoreFoundation APIs (which is unavoidable in Qt/Mac) has always had issues
0066  on Mac OS X, but as of 10.5 is explicitly disallowed with an exception.  As a
0067  result, in the case where we would normally fork and then dlopen code, or continue
0068  to run other code, we must now fork-and-exec.
0069 
0070  See "CoreFoundation and fork()" at http://developer.apple.com/releasenotes/CoreFoundation/CoreFoundation.html
0071 */
0072 
0073 void
0074 mac_fork_and_reexec_self()
0075 {
0076     int argc = *_NSGetArgc();
0077     char **argv = *_NSGetArgv();
0078     char *newargv[argc + 2];
0079     char progname[PATH_MAX];
0080     uint32_t buflen = PATH_MAX;
0081     _NSGetExecutablePath(progname, &buflen);
0082     bool found_psn = false;
0083 
0084     for (int i = 0; i < argc; i++) {
0085         newargv[i] = argv[i];
0086     }
0087 
0088     newargv[argc] = "--nofork";
0089     newargv[argc + 1] = NULL;
0090 
0091     int x_fork_result = fork();
0092     switch (x_fork_result) {
0093 
0094     case -1:
0095 #ifndef NDEBUG
0096         fprintf(stderr, "Mac OS X workaround fork() failed!\n");
0097 #endif
0098         ::_exit(255);
0099         break;
0100 
0101     case 0:
0102         // Child
0103         execvp(progname, newargv);
0104         break;
0105 
0106     default:
0107         // Parent
0108         _exit(0);
0109         break;
0110 
0111     }
0112 }
0113 
0114 /**
0115  Set the D-Bus environment based on session bus socket
0116 */
0117 
0118 bool mac_set_dbus_address(QString value)
0119 {
0120     if (!value.isEmpty() && QFile::exists(value) && (QFile::permissions(value) & QFile::WriteUser)) {
0121         value = QLatin1String("unix:path=") + value;
0122         qputenv("DBUS_SESSION_BUS_ADDRESS", value.toLocal8Bit());
0123         kDebug() << "set session bus address to" << value;
0124         return true;
0125     }
0126     return false;
0127 }
0128 
0129 /**
0130  Make sure D-Bus is initialized, by any means necessary.
0131 */
0132 
0133 void mac_initialize_dbus()
0134 {
0135     if (dbus_initialized) {
0136         return;
0137     }
0138 
0139     QString dbusVar = QString::fromLocal8Bit(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
0140     if (!dbusVar.isEmpty()) {
0141         dbus_initialized = true;
0142         return;
0143     }
0144 
0145     dbusVar = QFile::decodeName(qgetenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET"));
0146     if (mac_set_dbus_address(dbusVar)) {
0147         dbus_initialized = true;
0148         return;
0149     }
0150 
0151     QString externalProc;
0152     QStringList path = QFile::decodeName(qgetenv("KDEDIRS")).split(QLatin1Char(':')).replaceInStrings(QRegExp(QLatin1String("$")), QLatin1String("/bin"));
0153     path << QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':')) << QLatin1String("/usr/local/bin");
0154 
0155     for (int i = 0; i < path.size(); ++i) {
0156         QString testLaunchctl = QString(path.at(i)).append(QLatin1String("/launchctl"));
0157         if (QFile(testLaunchctl).exists()) {
0158             externalProc = testLaunchctl;
0159             break;
0160         }
0161     }
0162 
0163     if (!externalProc.isEmpty()) {
0164         QProcess qp;
0165         qp.setTextModeEnabled(true);
0166 
0167         qp.start(externalProc, QStringList() << QLatin1String("getenv") << QLatin1String("DBUS_LAUNCHD_SESSION_BUS_SOCKET"));
0168         if (!qp.waitForFinished(timeout)) {
0169             kDebug() << "error running" << externalProc << qp.errorString();
0170             return;
0171         }
0172         if (qp.exitCode() != 0) {
0173             kDebug() << externalProc << "unsuccessful:" << qp.readAllStandardError();
0174             return;
0175         }
0176 
0177         QString line = QString::fromLatin1(qp.readLine()).trimmed(); // read the first line
0178         if (mac_set_dbus_address(line)) {
0179             dbus_initialized = true;    // hooray
0180         }
0181     }
0182 
0183     if (dbus_initialized == false) {
0184         kDebug() << "warning: unable to initialize D-Bus environment!";
0185     }
0186 
0187 }
0188 
0189 QString mac_app_filename()
0190 {
0191     static QString appFileName;
0192     if (appFileName.isEmpty()) {
0193         CFURLRef bundleURL = NULL;
0194         CFBundleRef bundle = NULL;
0195         CFStringRef bundlePath = NULL;
0196 
0197         bundle = CFBundleGetMainBundle();
0198         if (bundle) {
0199             bundleURL = CFBundleCopyBundleURL(bundle);
0200             bundlePath = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
0201 
0202             if (bundleURL) {
0203                 CFRelease(bundleURL);
0204             }
0205 
0206             if (bundlePath) {
0207                 appFileName = convert_CFString_to_QString(bundlePath);
0208                 CFRelease(bundlePath);
0209             }
0210         }
0211     }
0212     return appFileName;
0213 }
0214 
0215 #endif