Warning, /frameworks/kinit/src/kdeinit/kinit_mac.mm is written in an unsupported language. File is not indexed.
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999-2000 Waldo Bastian <bastian@kde.org> 0004 SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@sime.com> 0005 SPDX-FileCopyrightText: 2001 Lubos Lunak <l.lunak@kde.org> 0006 SPDX-FileCopyrightText: 2015, 2016 René J.V. Bertin <rjvbertin@gmail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-only 0009 */ 0010 0011 #include <config-kdeinit.h> 0012 0013 #include <sys/types.h> 0014 #include <sys/time.h> 0015 #include <sys/resource.h> 0016 #include <sys/stat.h> 0017 #include <sys/socket.h> 0018 #include <sys/un.h> 0019 #include <sys/wait.h> 0020 #if HAVE_SYS_SELECT_H 0021 #include <sys/select.h> // Needed on some systems. 0022 #endif 0023 0024 #include <ctype.h> 0025 #include <errno.h> 0026 #include <fcntl.h> 0027 #include "proctitle.h" 0028 #include <dlfcn.h> 0029 #include <signal.h> 0030 #include <stdio.h> 0031 #include <stdlib.h> 0032 #include <string.h> 0033 #include <unistd.h> 0034 #include <locale.h> 0035 0036 #include <QtCore/QLibrary> 0037 #include <QtCore/QString> 0038 #include <QtCore/QFile> 0039 #include <QtCore/QDate> 0040 #include <QtCore/QDir> 0041 #include <QtCore/QFileInfo> 0042 #include <QtCore/QRegExp> 0043 #include <QCoreApplication> 0044 #include <QFont> 0045 #include <KConfig> 0046 #include <KLocalizedString> 0047 #include <QDebug> 0048 #include <QSaveFile> 0049 0050 #import <Foundation/Foundation.h> 0051 #import <AppKit/AppKit.h> 0052 #include <CoreFoundation/CFBundle.h> 0053 #include <CoreFoundation/CFString.h> 0054 #include <CoreFoundation/CFURL.h> 0055 #include <crt_externs.h> // for _NSGetArgc and friends 0056 #include <mach-o/dyld.h> // for _NSGetExecutablePath 0057 0058 #include <kinit_version.h> 0059 0060 #include "klauncher_cmds.h" 0061 0062 #include <QStandardPaths> 0063 0064 #include "kinit.h" 0065 #include "kinit_mac.h" 0066 0067 extern char **environ; 0068 0069 static int show_message_box( const QString &title, const QString &message ) 0070 { 0071 int (*_CGSDefaultConnection_p)(); 0072 _CGSDefaultConnection_p = (int (*)()) dlsym(RTLD_DEFAULT, "_CGSDefaultConnection"); 0073 if (!_CGSDefaultConnection_p) { 0074 NSLog(@"qt_fatal_message_box(%@,%@): couldn't load _CGSDefaultConnection: %s", 0075 [NSString stringWithCString:message.toUtf8().constData() encoding:NSUTF8StringEncoding], 0076 [NSString stringWithCString:title.toUtf8().constData() encoding:NSUTF8StringEncoding], dlerror()); 0077 } 0078 if (_CGSDefaultConnection_p && (*_CGSDefaultConnection_p)()) { 0079 @autoreleasepool { 0080 NSAlert* alert = [[[NSAlert alloc] init] autorelease]; 0081 NSString *msg, *tit; 0082 @synchronized([NSAlert class]){ 0083 [alert addButtonWithTitle:@"OK"]; 0084 [alert setAlertStyle:NSCriticalAlertStyle]; 0085 [alert setMessageText:@"" ]; 0086 if( !(msg = [NSString stringWithCString:message.toUtf8().constData() encoding:NSUTF8StringEncoding]) ){ 0087 msg = [NSString stringWithCString:message.toLatin1().constData() encoding:NSASCIIStringEncoding]; 0088 } 0089 if( !(tit = [NSString stringWithCString:title.toUtf8().constData() encoding:NSUTF8StringEncoding]) ){ 0090 tit = [NSString stringWithCString:title.toLatin1().constData() encoding:NSASCIIStringEncoding]; 0091 } 0092 if( msg ){ 0093 [alert setInformativeText:msg]; 0094 } 0095 else{ 0096 NSLog( @"msg=%@ tit=%@", msg, tit ); 0097 } 0098 [[alert window] setTitle:tit]; 0099 return NSAlertSecondButtonReturn == [alert runModal]; 0100 } 0101 } 0102 } 0103 return 0; 0104 } 0105 0106 static void exitWithErrorMsg(const QString &errorMsg, bool doExit) 0107 { 0108 fprintf(stderr, "%s\n", errorMsg.toLocal8Bit().data()); 0109 QByteArray utf8ErrorMsg = errorMsg.toUtf8(); 0110 d.result = 3; // Error with msg 0111 write(d.fd[1], &d.result, 1); 0112 int l = utf8ErrorMsg.length(); 0113 write(d.fd[1], &l, sizeof(int)); 0114 write(d.fd[1], utf8ErrorMsg.data(), l); 0115 close(d.fd[1]); 0116 show_message_box(QStringLiteral("kdeinit5"), errorMsg); 0117 if (doExit) { 0118 exit(255); 0119 } 0120 } 0121 0122 pid_t launch(int argc, const char *_name, const char *args, 0123 const char *cwd, int envc, const char *envs, 0124 bool reset_env, const char *tty, bool avoid_loops, 0125 const char *startup_id_str) // krazy:exclude=doublequote_chars 0126 { 0127 QString bin, libpath; 0128 QByteArray name; 0129 QByteArray execpath; 0130 0131 if (_name[0] != '/') { 0132 name = _name; 0133 bin = QFile::decodeName(name); 0134 execpath = execpath_avoid_loops(name, envc, envs, avoid_loops); 0135 } else { 0136 name = _name; 0137 bin = QFile::decodeName(name); 0138 name = name.mid(name.lastIndexOf('/') + 1); 0139 0140 // FIXME: this .so extension stuff is very Linux-specific 0141 // a .so extension can occur on OS X, but the typical extension is .dylib 0142 if (bin.endsWith(QStringLiteral(".so")) || bin.endsWith(QStringLiteral(".dylib"))) { 0143 libpath = bin; 0144 } else { 0145 execpath = _name; 0146 } 0147 } 0148 #ifndef NDEBUG 0149 fprintf(stderr, "kdeinit5: preparing to launch '%s'\n", libpath.isEmpty() 0150 ? execpath.constData() : libpath.toUtf8().constData()); 0151 #endif 0152 if (!args) { 0153 argc = 1; 0154 } 0155 0156 // do certain checks before forking 0157 if (execpath.isEmpty()) { 0158 QString errorMsg = i18n("Could not find '%1' executable.", QFile::decodeName(_name)); 0159 exitWithErrorMsg(errorMsg, false); 0160 d.result = 3; 0161 d.fork = 0; 0162 return d.fork; 0163 } else if (!libpath.isEmpty()) { 0164 QString errorMsg = i18n("Launching library '%1' is not supported on OS X.", libpath); 0165 exitWithErrorMsg(errorMsg, false); 0166 d.result = 3; 0167 d.fork = 0; 0168 return d.fork; 0169 } 0170 0171 if (0 > pipe(d.fd)) { 0172 perror("kdeinit5: pipe() failed"); 0173 d.result = 3; 0174 d.errorMsg = i18n("Unable to start new process.\n" 0175 "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(); 0176 d.fork = 0; 0177 return d.fork; 0178 } 0179 0180 // find out this path before forking, doing it afterwards 0181 // crashes on some platforms, notably OSX 0182 const QString bundlepath = QStandardPaths::findExecutable(QFile::decodeName(execpath)); 0183 0184 const QString argvexe = QStandardPaths::findExecutable(QLatin1String(_name)); 0185 0186 d.errorMsg = 0; 0187 d.fork = fork(); 0188 switch (d.fork) { 0189 case -1: 0190 perror("kdeinit5: fork() failed"); 0191 d.result = 3; 0192 d.errorMsg = i18n("Unable to create new process.\n" 0193 "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(); 0194 close(d.fd[0]); 0195 close(d.fd[1]); 0196 d.fork = 0; 0197 break; 0198 case 0: { 0199 /** Child **/ 0200 close(d.fd[0]); 0201 close_fds(); 0202 reset_oom_protect(); 0203 0204 // Try to chdir, either to the requested directory or to the user's document path by default. 0205 // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist, 0206 // we still want to execute `foo` even if the chdir() failed. 0207 if (cwd && *cwd) { 0208 (void)chdir(cwd); 0209 } 0210 0211 if (reset_env) { // KWRAPPER/SHELL 0212 0213 QList<QByteArray> unset_envs; 0214 for (int tmp_env_count = 0; 0215 environ[tmp_env_count]; 0216 tmp_env_count++) { 0217 unset_envs.append(environ[ tmp_env_count ]); 0218 } 0219 for (const QByteArray &tmp : std::as_const(unset_envs)) { 0220 int pos = tmp.indexOf('='); 0221 if (pos >= 0) { 0222 unsetenv(tmp.left(pos).constData()); 0223 } 0224 } 0225 } 0226 0227 for (int i = 0; i < envc; i++) { 0228 putenv((char *)envs); 0229 while (*envs != 0) { 0230 envs++; 0231 } 0232 envs++; 0233 } 0234 0235 { 0236 QByteArray procTitle; 0237 // launching an executable: can do an exec directly 0238 d.argv = (char **) malloc(sizeof(char *) * (argc + 1)); 0239 if (!argvexe.isEmpty()) { 0240 QByteArray cstr = argvexe.toLocal8Bit(); 0241 d.argv[0] = strdup(cstr.data()); 0242 } else { 0243 d.argv[0] = (char *) _name; 0244 } 0245 for (int i = 1; i < argc; i++) { 0246 d.argv[i] = (char *) args; 0247 procTitle += ' '; 0248 procTitle += (char *) args; 0249 while (*args != 0) { 0250 args++; 0251 } 0252 args++; 0253 } 0254 d.argv[argc] = 0; 0255 0256 #ifndef SKIP_PROCTITLE 0257 /** Give the process a new name **/ 0258 proctitle_set("%s%s", name.data(), procTitle.data() ? procTitle.data() : ""); 0259 #endif 0260 } 0261 0262 if (!execpath.isEmpty()) { 0263 // we're launching an executable; even after a fork and being exec'ed, that 0264 // executable is allowed to use non-POSIX APIs. 0265 d.result = 2; // Try execing 0266 write(d.fd[1], &d.result, 1); 0267 0268 // We set the close on exec flag. 0269 // Closing of d.fd[1] indicates that the execvp succeeded! 0270 fcntl(d.fd[1], F_SETFD, FD_CLOEXEC); 0271 0272 setup_tty(tty); 0273 0274 QByteArray executable = execpath; 0275 if (!bundlepath.isEmpty()) { 0276 executable = QFile::encodeName(bundlepath); 0277 } 0278 // TODO: we probably want to use [[NSProcessInfo processInfo] setProcessName:NSString*] here to 0279 // make the new app show up under its own name in non-POSIX process listings. 0280 0281 if (!executable.isEmpty()) { 0282 #ifndef NDEBUG 0283 qDebug() << "execvp" << executable; 0284 for (int i = 0 ; i < argc ; ++i) { 0285 qDebug() << "arg #" << i << "=" << d.argv[i]; 0286 } 0287 #endif 0288 // attempt to the correct application name 0289 QFileInfo fi(QString::fromUtf8(executable)); 0290 [[NSProcessInfo processInfo] setProcessName:(NSString*)fi.baseName().toCFString()]; 0291 qApp->setApplicationName(fi.baseName()); 0292 execvp(executable.constData(), d.argv); 0293 } 0294 0295 d.result = 1; // Error 0296 write(d.fd[1], &d.result, 1); 0297 close(d.fd[1]); 0298 exit(255); 0299 } 0300 0301 break; 0302 } 0303 default: 0304 /** Parent **/ 0305 close(d.fd[1]); 0306 bool exec = false; 0307 for (;;) { 0308 d.n = read(d.fd[0], &d.result, 1); 0309 if (d.n == 1) { 0310 if (d.result == 2) { 0311 #ifndef NDEBUG 0312 //fprintf(stderr, "kdeinit5: no kdeinit module, trying exec....\n"); 0313 #endif 0314 exec = true; 0315 continue; 0316 } 0317 if (d.result == 3) { 0318 int l = 0; 0319 d.n = read(d.fd[0], &l, sizeof(int)); 0320 if (d.n == sizeof(int)) { 0321 QByteArray tmp; 0322 tmp.resize(l + 1); 0323 d.n = read(d.fd[0], tmp.data(), l); 0324 tmp[l] = 0; 0325 if (d.n == l) { 0326 d.errorMsg = tmp; 0327 } 0328 } 0329 } 0330 // Finished 0331 break; 0332 } 0333 if (d.n == -1) { 0334 if (errno == ECHILD) { // a child died. 0335 continue; 0336 } 0337 if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read 0338 continue; 0339 } 0340 } 0341 if (d.n == 0) { 0342 if (exec) { 0343 d.result = 0; 0344 } else { 0345 fprintf(stderr, "kdeinit5: (%s %s) Pipe closed unexpectedly", name.constData(), execpath.constData()); 0346 perror("kdeinit5: Pipe closed unexpectedly"); 0347 d.result = 1; // Error 0348 } 0349 break; 0350 } 0351 perror("kdeinit5: Error reading from pipe"); 0352 d.result = 1; // Error 0353 break; 0354 } 0355 close(d.fd[0]); 0356 } 0357 return d.fork; 0358 } 0359 0360 /** 0361 Calling CoreFoundation and other non-POSIX APIs (which is unavoidable) has always caused issues 0362 with fork/exec on Mac OS X, but as of 10.5 is explicitly disallowed with an exception. As a 0363 result, in the case where we would normally fork and then dlopen code, or continue 0364 to run other code, we must now fork-and-exec, and even then we need to use a helper (proxy) 0365 to launch the actual application we wish to launch, a proxy that will only have used POSIX APIs. 0366 This probably cancels the whole idea of preloading libraries, but it is as it is. 0367 Note that this function is called only when kdeinit5 is forking itself, 0368 in order to disappear into the background. 0369 */ 0370 // Copied from kkernel_mac.cpp 0371 void mac_fork_and_reexec_self() 0372 { 0373 int argc = *_NSGetArgc(); 0374 char **argv = *_NSGetArgv(); 0375 char *newargv[argc + 2]; 0376 char progname[PATH_MAX]; 0377 uint32_t buflen = PATH_MAX; 0378 _NSGetExecutablePath(progname, &buflen); 0379 0380 for (int i = 0; i < argc; i++) { 0381 newargv[i] = argv[i]; 0382 } 0383 0384 newargv[argc] = (char*)"--nofork"; 0385 newargv[argc + 1] = 0; 0386 0387 int x_fork_result = fork(); 0388 switch (x_fork_result) { 0389 0390 case -1: 0391 #ifndef NDEBUG 0392 fprintf(stderr, "Mac OS X workaround fork() failed!\n"); 0393 #endif 0394 ::_exit(255); 0395 break; 0396 0397 case 0: 0398 // Child 0399 execvp(progname, newargv); 0400 break; 0401 0402 default: 0403 // Parent 0404 _exit(0); 0405 break; 0406 0407 } 0408 }