Warning, file /frameworks/kinit/src/kdeinit/kinit.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include <config-kdeinit.h>
0011 
0012 #include <sys/types.h>
0013 #include <sys/time.h>
0014 #include <sys/resource.h>
0015 #include <sys/stat.h>
0016 #include <sys/socket.h>
0017 #include <sys/un.h>
0018 #include <sys/wait.h>
0019 #if HAVE_SYS_SELECT_H
0020 #include <sys/select.h>     // Needed on some systems.
0021 #endif
0022 
0023 #include <ctype.h>
0024 #include <cerrno>
0025 #include <fcntl.h>
0026 #include "proctitle.h"
0027 #include <signal.h>
0028 #include <stdio.h>
0029 #include <stdlib.h>
0030 #include <string.h>
0031 #include <unistd.h>
0032 #include <locale.h>
0033 
0034 #include <QLibrary>
0035 #include <QString>
0036 #include <QFile>
0037 #include <QDate>
0038 #include <QDir>
0039 #include <QFileInfo>
0040 #include <QRegExp>
0041 #include <QFont>
0042 #include <KCrash>
0043 #include <KConfig>
0044 #include <KLocalizedString>
0045 #include <QDebug>
0046 #include <QSaveFile>
0047 
0048 #ifdef Q_OS_LINUX
0049 #include <sys/prctl.h>
0050 #ifndef PR_SET_NAME
0051 #define PR_SET_NAME 15
0052 #endif
0053 #endif
0054 
0055 #include <kinit_version.h>
0056 
0057 #include "klauncher_cmds.h"
0058 
0059 #if HAVE_X11
0060 #include <X11/Xlib.h>
0061 #include <X11/Xatom.h>
0062 #include <fixx11h.h>
0063 #endif
0064 
0065 #if HAVE_XCB
0066 #include <NETWM>
0067 #include <xcb/xcb.h>
0068 #endif
0069 
0070 #include <kstartupinfo.h>
0071 
0072 #include <QStandardPaths>
0073 
0074 #include "kinit.h"
0075 #ifdef Q_OS_OSX
0076 #include "kinit_mac.h"
0077 #endif
0078 
0079 #ifdef Q_OS_UNIX
0080 //TODO: make sure what libraries we want here...
0081 static const char *extra_libs[] = {
0082 #ifdef Q_OS_OSX
0083     "libKF5KIOCore.5.dylib",
0084     "libKF5Parts.5.dylib",
0085     "libKF5Plasma.5.dylib"
0086 #else
0087     "libKF5KIOCore.so.5",
0088     "libKF5Parts.so.5",
0089     "libKF5Plasma.so.5"
0090 #endif
0091 };
0092 #endif
0093 
0094 // #define SKIP_PROCTITLE 1
0095 
0096 namespace KCrash
0097 {
0098 extern KCRASH_EXPORT bool loadedByKdeinit;
0099 }
0100 
0101 extern char **environ;
0102 
0103 #if HAVE_X11
0104 static int X11fd = -1;
0105 static Display *X11display = nullptr;
0106 static int X11_startup_notify_fd = -1;
0107 #endif
0108 #if HAVE_XCB
0109 static xcb_connection_t *s_startup_notify_connection = nullptr;
0110 static int s_startup_notify_screen = 0;
0111 #endif
0112 // Finds size of sun_path without allocating a sockaddr_un to do it.
0113 // Assumes sun_path is at end of sockaddr_un though
0114 #define MAX_SOCK_FILE (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un,sun_path))
0115 static char sock_file[MAX_SOCK_FILE];
0116 
0117 // Can't use QGuiApplication::platformName() here, there is no app instance.
0118 #if HAVE_X11 || HAVE_XCB
0119 static const char* displayEnvVarName_c()
0120 {
0121     return "DISPLAY";
0122 }
0123 static inline QByteArray displayEnvVarName()
0124 {
0125     return QByteArray::fromRawData(displayEnvVarName_c(), strlen(displayEnvVarName_c()));
0126 }
0127 #endif
0128 
0129 static struct child *children;
0130 
0131 #if HAVE_X11
0132 extern "C" {
0133     int kdeinit_xio_errhandler(Display *);
0134     int kdeinit_x_errhandler(Display *, XErrorEvent *err);
0135 }
0136 #endif
0137 
0138 static int oom_pipe = -1;
0139 
0140 /*
0141  * Clean up the file descriptor table by closing all file descriptors
0142  * that are still open.
0143  *
0144  * This function is called very early in the main() function, so that
0145  * we don't leak anything that was leaked to us.
0146  */
0147 static void cleanup_fds()
0148 {
0149 #if HAVE_CLOSE_RANGE
0150     if (oom_pipe >= 3) {
0151         if (oom_pipe > 3) {
0152             close_range(3, oom_pipe - 1, 0);
0153         }
0154         close_range(oom_pipe + 1, ~0, 0);
0155     } else {
0156         close_range(3, ~0, 0);
0157     }
0158 #else
0159     int maxfd = FD_SETSIZE;
0160     struct rlimit rl;
0161     if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
0162         maxfd = rl.rlim_cur;
0163     }
0164     for (int fd = 3; fd < maxfd; ++fd) {
0165         if (fd != oom_pipe)
0166             close(fd);
0167     }
0168 #endif
0169 }
0170 
0171 /*
0172  * Close fd's which are only useful for the parent process.
0173  * Restore default signal handlers.
0174  */
0175 void close_fds()
0176 {
0177     while (struct child *child = children) {
0178         close(child->sock);
0179         children = child->next;
0180         free(child);
0181     }
0182 
0183     if (d.deadpipe[0] != -1) {
0184         close(d.deadpipe[0]);
0185         d.deadpipe[0] = -1;
0186     }
0187 
0188     if (d.deadpipe[1] != -1) {
0189         close(d.deadpipe[1]);
0190         d.deadpipe[1] = -1;
0191     }
0192 
0193     if (d.initpipe[0] != -1) {
0194         close(d.initpipe[0]);
0195         d.initpipe[0] = -1;
0196     }
0197 
0198     if (d.initpipe[1] != -1) {
0199         close(d.initpipe[1]);
0200         d.initpipe[1] = -1;
0201     }
0202 
0203     if (d.launcher[0] != -1) {
0204         close(d.launcher[0]);
0205         d.launcher[0] = -1;
0206     }
0207     if (d.wrapper != -1) {
0208         close(d.wrapper);
0209         d.wrapper = -1;
0210     }
0211     if (d.accepted_fd != -1) {
0212         close(d.accepted_fd);
0213         d.accepted_fd = -1;
0214     }
0215 #if HAVE_X11
0216     if (X11fd >= 0) {
0217         close(X11fd);
0218         X11fd = -1;
0219     }
0220     if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd) {
0221         close(X11_startup_notify_fd);
0222         X11_startup_notify_fd = -1;
0223     }
0224 #endif
0225 
0226     signal(SIGCHLD, SIG_DFL);
0227     signal(SIGPIPE, SIG_DFL);
0228 }
0229 
0230 /* Notify wrapper program that the child it started has finished. */
0231 static void child_died(pid_t exit_pid, int exit_status)
0232 {
0233     struct child *child, **childptr = &children;
0234 
0235     while ((child = *childptr)) {
0236         if (child->pid == exit_pid) {
0237             // Send a message with the return value of the child on the control socket
0238             klauncher_header request_header;
0239             long request_data[2];
0240             request_header.cmd = LAUNCHER_CHILD_DIED;
0241             request_header.arg_length = sizeof(long) * 2;
0242             request_data[0] = exit_pid;
0243             request_data[1] = exit_status;
0244             write(child->sock, &request_header, sizeof(request_header));
0245             write(child->sock, request_data, request_header.arg_length);
0246             close(child->sock);
0247 
0248             *childptr = child->next;
0249             free(child);
0250             return;
0251         }
0252 
0253         childptr = &child->next;
0254     }
0255 }
0256 
0257 void setup_tty(const char *tty)
0258 {
0259     if (tty == nullptr || *tty == '\0') {
0260         return;
0261     }
0262     QFile f(QFile::decodeName(tty));
0263     f.open(QFileDevice::WriteOnly);
0264     int fd = f.handle();
0265 
0266     if (fd < 0) {
0267         perror("kdeinit5: could not open() tty");
0268         return;
0269     }
0270     if (dup2(fd, STDOUT_FILENO) < 0) {
0271         perror("kdeinit5: could not dup2() stdout tty");
0272     }
0273     if (dup2(fd, STDERR_FILENO) < 0) {
0274         perror("kdeinit5: could not dup2() stderr tty");
0275     }
0276     close(fd);
0277 }
0278 
0279 // var has to be e.g. "DISPLAY=", i.e. with =
0280 const char *get_env_var(const char *var, int envc, const char *envs)
0281 {
0282     if (envc > 0) {
0283         // get the var from envs
0284         const char *env_l = envs;
0285         int ln = strlen(var);
0286         for (int i = 0;  i < envc; i++) {
0287             if (strncmp(env_l, var, ln) == 0) {
0288                 return env_l + ln;
0289             }
0290             while (*env_l != 0) {
0291                 env_l++;
0292             }
0293             env_l++;
0294         }
0295     }
0296     return nullptr;
0297 }
0298 
0299 #if HAVE_XCB
0300 static void init_startup_info(KStartupInfoId &id, const QByteArray &bin,
0301                               int envc, const char *envs)
0302 {
0303     QByteArray envname = displayEnvVarName() + "=";
0304     const char *dpy = get_env_var(envname.constData(), envc, envs);
0305     // this may be called in a child, so it can't use display open using X11display
0306     // also needed for multihead
0307     s_startup_notify_connection = xcb_connect(dpy, &s_startup_notify_screen);
0308     if (!s_startup_notify_connection) {
0309         return;
0310     }
0311     if (xcb_connection_has_error(s_startup_notify_connection)) {
0312         xcb_disconnect(s_startup_notify_connection);
0313         s_startup_notify_connection = nullptr;
0314         return;
0315     }
0316     X11_startup_notify_fd = xcb_get_file_descriptor(s_startup_notify_connection);
0317     NETRootInfo rootInfo(s_startup_notify_connection, NET::CurrentDesktop);
0318     KStartupInfoData data;
0319     data.setDesktop(rootInfo.currentDesktop(true));
0320     data.setBin(QFile::decodeName(bin));
0321     KStartupInfo::sendChangeXcb(s_startup_notify_connection, s_startup_notify_screen, id, data);
0322     xcb_flush(s_startup_notify_connection);
0323 }
0324 
0325 static void complete_startup_info(KStartupInfoId &id, pid_t pid)
0326 {
0327     if (!s_startup_notify_connection) {
0328         return;
0329     }
0330     if (pid == 0) { // failure
0331         KStartupInfo::sendFinishXcb(s_startup_notify_connection, s_startup_notify_screen, id);
0332     } else {
0333         KStartupInfoData data;
0334         data.addPid(pid);
0335         data.setHostname();
0336         KStartupInfo::sendChangeXcb(s_startup_notify_connection, s_startup_notify_screen, id, data);
0337     }
0338     xcb_disconnect(s_startup_notify_connection);
0339     s_startup_notify_connection = nullptr;
0340     s_startup_notify_screen = 0;
0341     X11_startup_notify_fd = -1;
0342 }
0343 #endif
0344 
0345 QByteArray execpath_avoid_loops(const QByteArray &exec, int envc, const char *envs, bool avoid_loops)
0346 {
0347     QStringList paths;
0348     const QRegExp pathSepRegExp(QStringLiteral("[:\b]"));
0349     if (envc > 0) { // use the passed environment
0350         const char *path = get_env_var("PATH=", envc, envs);
0351         if (path != nullptr) {
0352             paths = QFile::decodeName(path).split(pathSepRegExp);
0353         }
0354     } else {
0355         paths = QString::fromLocal8Bit(qgetenv("PATH")).split(pathSepRegExp, Qt::KeepEmptyParts);
0356         paths.prepend(QFile::decodeName(KDE_INSTALL_FULL_LIBEXECDIR_KF5));
0357     }
0358     QString execpath = QStandardPaths::findExecutable(QFile::decodeName(exec), paths);
0359     if (avoid_loops && !execpath.isEmpty()) {
0360         const int pos = execpath.lastIndexOf(QLatin1Char('/'));
0361         const QString bin_path = execpath.left(pos);
0362         for (QStringList::Iterator it = paths.begin();
0363                 it != paths.end();
0364                 ++it) {
0365             if (*it == bin_path || *it == bin_path + QLatin1Char('/')) {
0366                 paths.erase(it);
0367                 break; // -->
0368             }
0369         }
0370         execpath = QStandardPaths::findExecutable(QFile::decodeName(exec), paths);
0371     }
0372     return QFile::encodeName(execpath);
0373 }
0374 
0375 #if KDEINIT_OOM_PROTECT
0376 static void oom_protect_sighandler(int)
0377 {
0378 }
0379 
0380 void reset_oom_protect()
0381 {
0382     if (oom_pipe <= 0) {
0383         return;
0384     }
0385     struct sigaction act, oldact;
0386     act.sa_handler = oom_protect_sighandler;
0387     act.sa_flags = 0;
0388     sigemptyset(&act.sa_mask);
0389     sigaction(SIGUSR1, &act, &oldact);
0390     sigset_t sigs, oldsigs;
0391     sigemptyset(&sigs);
0392     sigaddset(&sigs, SIGUSR1);
0393     sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
0394     pid_t pid = getpid();
0395     if (write(oom_pipe, &pid, sizeof(pid_t)) > 0) {
0396         struct timespec timeout;
0397         timeout.tv_sec = 1;
0398         timeout.tv_nsec = 0;
0399         do {
0400             if (sigtimedwait(&sigs, nullptr, &timeout) < 0) { // wait for the signal to come
0401                 if (errno == EINTR) {
0402                     // other signal
0403                     continue;
0404                 } else if (errno == EAGAIN) {
0405                     fprintf(stderr, "Timed out during reset OOM protection: %d\n", pid);
0406                 } else {
0407                     fprintf(stderr, "Error during reset OOM protection: %d\n", pid);
0408                 }
0409             }
0410             break;
0411         } while (true);
0412     } else {
0413 #ifndef NDEBUG
0414         fprintf(stderr, "Failed to reset OOM protection: %d\n", pid);
0415 #endif
0416     }
0417     sigprocmask(SIG_SETMASK, &oldsigs, nullptr);
0418     sigaction(SIGUSR1, &oldact, nullptr);
0419     close(oom_pipe);
0420     oom_pipe = -1;
0421 }
0422 #else
0423 void reset_oom_protect()
0424 {
0425 }
0426 #endif
0427 
0428 #ifndef Q_OS_OSX
0429 
0430 static pid_t launch(int argc, const char *_name, const char *args,
0431                     const char *cwd = nullptr, int envc = 0, const char *envs = nullptr,
0432                     bool reset_env = false,
0433                     const char *tty = nullptr, bool avoid_loops = false,
0434                     const char *startup_id_str = "0")  // krazy:exclude=doublequote_chars
0435 {
0436     QByteArray name = _name;
0437     QByteArray execpath;
0438 
0439     if (name[0] != '/') {
0440         execpath = execpath_avoid_loops(name, envc, envs, avoid_loops);
0441     } else {
0442         name = name.mid(name.lastIndexOf('/') + 1);
0443         execpath = _name;
0444     }
0445 #ifndef NDEBUG
0446     fprintf(stderr, "kdeinit5: preparing to launch '%s'\n", execpath.constData());
0447 #endif
0448     if (!args) {
0449         argc = 1;
0450     }
0451 
0452     if (0 > pipe(d.fd)) {
0453         perror("kdeinit5: pipe() failed");
0454         d.result = 3;
0455         d.errorMsg = i18n("Unable to start new process.\n"
0456                           "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();
0457         d.fork = 0;
0458         return d.fork;
0459     }
0460 
0461 #if HAVE_XCB
0462     KStartupInfoId startup_id;
0463     startup_id.initId(startup_id_str);
0464     if (!startup_id.isNull()) {
0465         init_startup_info(startup_id, name, envc, envs);
0466     }
0467 #endif
0468     // find out this path before forking, doing it afterwards
0469     // crashes on some platforms, notably OSX
0470 
0471     d.errorMsg = nullptr;
0472     d.fork = fork();
0473     switch (d.fork) {
0474     case -1:
0475         perror("kdeinit5: fork() failed");
0476         d.result = 3;
0477         d.errorMsg = i18n("Unable to create new process.\n"
0478                           "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();
0479         close(d.fd[0]);
0480         close(d.fd[1]);
0481         d.fork = 0;
0482         break;
0483     case 0: {
0484         /** Child **/
0485         close(d.fd[0]);
0486         close_fds();
0487         reset_oom_protect();
0488 
0489         // Try to chdir, either to the requested directory or to the user's document path by default.
0490         // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist,
0491         // we still want to execute `foo` even if the chdir() failed.
0492         if (cwd && *cwd) {
0493             (void)chdir(cwd);
0494         }
0495 
0496         if (reset_env) { // KWRAPPER/SHELL
0497 
0498             QList<QByteArray> unset_envs;
0499             for (int tmp_env_count = 0;
0500                     environ[tmp_env_count];
0501                     tmp_env_count++) {
0502                 unset_envs.append(environ[ tmp_env_count ]);
0503             }
0504             for (const QByteArray &tmp : std::as_const(unset_envs)) {
0505                 int pos = tmp.indexOf('=');
0506                 if (pos >= 0) {
0507                     unsetenv(tmp.left(pos).constData());
0508                 }
0509             }
0510         }
0511 
0512         for (int i = 0;  i < envc; i++) {
0513             putenv((char *)envs);
0514             while (*envs != 0) {
0515                 envs++;
0516             }
0517             envs++;
0518         }
0519 
0520 #if HAVE_X11
0521         if (startup_id.isNull()) {
0522             KStartupInfo::resetStartupEnv();
0523         } else {
0524             startup_id.setupStartupEnv();
0525         }
0526 #endif
0527         {
0528             int r;
0529             QByteArray procTitle;
0530             d.argv = (char **) malloc(sizeof(char *) * (argc + 1));
0531             d.argv[0] = (char *) _name;
0532             for (int i = 1;  i < argc; i++) {
0533                 d.argv[i] = (char *) args;
0534                 procTitle += ' ';
0535                 procTitle += (char *) args;
0536                 while (*args != 0) {
0537                     args++;
0538                 }
0539                 args++;
0540             }
0541             d.argv[argc] = nullptr;
0542 
0543 #ifndef SKIP_PROCTITLE
0544             // Give the process a new name
0545 #ifdef Q_OS_LINUX
0546             // set the process name, so that killall works like intended
0547             r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0);
0548             if (r == 0) {
0549                 proctitle_set("-%s [kdeinit5]%s", name.data(), procTitle.data() ? procTitle.data() : "");
0550             } else {
0551                 proctitle_set("%s%s", name.data(), procTitle.data() ? procTitle.data() : "");
0552             }
0553 #else
0554             proctitle_set("%s%s", name.data(), procTitle.data() ? procTitle.data() : "");
0555 #endif
0556 #endif
0557         }
0558 
0559         d.result = 2; // Try execing
0560         write(d.fd[1], &d.result, 1);
0561 
0562         // We set the close on exec flag.
0563         // Closing of d.fd[1] indicates that the execvp succeeded!
0564         fcntl(d.fd[1], F_SETFD, FD_CLOEXEC);
0565 
0566         setup_tty(tty);
0567 
0568         QByteArray executable = execpath;
0569         if (!executable.isEmpty()) {
0570             execvp(executable.constData(), d.argv);
0571         }
0572 
0573         d.result = 1; // Error
0574         write(d.fd[1], &d.result, 1);
0575         close(d.fd[1]);
0576         exit(255);
0577         break;
0578     }
0579     default:
0580         /** Parent **/
0581         close(d.fd[1]);
0582         bool exec = false;
0583         for (;;) {
0584             d.n = read(d.fd[0], &d.result, 1);
0585             if (d.n == 1) {
0586                 if (d.result == 2) {
0587 #ifndef NDEBUG
0588                     //fprintf(stderr, "kdeinit5: no kdeinit module, trying exec....\n");
0589 #endif
0590                     exec = true;
0591                     continue;
0592                 }
0593                 if (d.result == 3) {
0594                     int l = 0;
0595                     d.n = read(d.fd[0], &l, sizeof(int));
0596                     if (d.n == sizeof(int)) {
0597                         QByteArray tmp;
0598                         tmp.resize(l + 1);
0599                         d.n = read(d.fd[0], tmp.data(), l);
0600                         tmp[l] = 0;
0601                         if (d.n == l) {
0602                             d.errorMsg = tmp;
0603                         }
0604                     }
0605                 }
0606                 // Finished
0607                 break;
0608             }
0609             if (d.n == -1) {
0610                 if (errno == ECHILD) {  // a child died.
0611                     continue;
0612                 }
0613                 if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read
0614                     continue;
0615                 }
0616             }
0617             if (d.n == 0) {
0618                 if (exec) {
0619                     d.result = 0;
0620                 } else {
0621                     fprintf(stderr, "kdeinit5: (%s %s) Pipe closed unexpectedly", name.constData(), execpath.constData());
0622                     perror("kdeinit5: Pipe closed unexpectedly");
0623                     d.result = 1; // Error
0624                 }
0625                 break;
0626             }
0627             perror("kdeinit5: Error reading from pipe");
0628             d.result = 1; // Error
0629             break;
0630         }
0631         close(d.fd[0]);
0632     }
0633 #if HAVE_XCB
0634     if (!startup_id.isNull()) {
0635         if (d.fork && d.result == 0) { // launched successfully
0636             complete_startup_info(startup_id, d.fork);
0637         } else { // failure, cancel ASN
0638             complete_startup_info(startup_id, 0);
0639         }
0640     }
0641 #endif
0642     return d.fork;
0643 }
0644 #endif // !Q_OS_OSX
0645 
0646 extern "C" {
0647 
0648     static void sig_child_handler(int)
0649     {
0650         /*
0651          * Write into the pipe of death.
0652          * This way we are sure that we return from the select()
0653          *
0654          * A signal itself causes select to return as well, but
0655          * this creates a race-condition in case the signal arrives
0656          * just before we enter the select.
0657          */
0658         char c = 0;
0659         write(d.deadpipe[1], &c, 1);
0660     }
0661 
0662 }
0663 
0664 static void init_signals()
0665 {
0666     struct sigaction act;
0667     long options;
0668 
0669     if (pipe(d.deadpipe) != 0) {
0670         perror("kdeinit5: Aborting. Can not create pipe");
0671         exit(255);
0672     }
0673 
0674     options = fcntl(d.deadpipe[0], F_GETFL);
0675     if (options == -1) {
0676         perror("kdeinit5: Aborting. Can not make pipe non-blocking");
0677         exit(255);
0678     }
0679 
0680     if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1) {
0681         perror("kdeinit5: Aborting. Can not make pipe non-blocking");
0682         exit(255);
0683     }
0684 
0685     /*
0686      * A SIGCHLD handler is installed which sends a byte into the
0687      * pipe of death. This is to ensure that a dying child causes
0688      * an exit from select().
0689      */
0690     act.sa_handler = sig_child_handler;
0691     sigemptyset(&(act.sa_mask));
0692     sigaddset(&(act.sa_mask), SIGCHLD);
0693     sigprocmask(SIG_UNBLOCK, &(act.sa_mask), nullptr);
0694     act.sa_flags = SA_NOCLDSTOP;
0695 
0696     // CC: take care of SunOS which automatically restarts interrupted system
0697     // calls (and thus does not have SA_RESTART)
0698 
0699 #ifdef SA_RESTART
0700     act.sa_flags |= SA_RESTART;
0701 #endif
0702     sigaction(SIGCHLD, &act, nullptr);
0703 
0704     act.sa_handler = SIG_IGN;
0705     sigemptyset(&(act.sa_mask));
0706     sigaddset(&(act.sa_mask), SIGPIPE);
0707     sigprocmask(SIG_UNBLOCK, &(act.sa_mask), nullptr);
0708     act.sa_flags = 0;
0709     sigaction(SIGPIPE, &act, nullptr);
0710 }
0711 
0712 static void init_kdeinit_socket()
0713 {
0714     struct sockaddr_un sa;
0715     kde_socklen_t socklen;
0716     long options;
0717     const QByteArray home_dir = qgetenv("HOME");
0718     int max_tries = 10;
0719     if (home_dir.isEmpty()) {
0720         fprintf(stderr, "kdeinit5: Aborting. $HOME not set!");
0721         exit(255);
0722     }
0723     if (chdir(home_dir.constData()) != 0) {
0724         fprintf(stderr, "kdeinit5: Aborting. Couldn't enter '%s'!", home_dir.constData());
0725         exit(255);
0726     }
0727 
0728     {
0729         const QByteArray path = home_dir;
0730         const QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
0731         if (access(path.data(), R_OK | W_OK)) {
0732             if (errno == ENOENT) {
0733                 fprintf(stderr, "kdeinit5: Aborting. $HOME directory (%s) does not exist.\n", path.data());
0734                 exit(255);
0735             } else if (readOnly.isEmpty()) {
0736                 fprintf(stderr, "kdeinit5: Aborting. No write access to $HOME directory (%s).\n", path.data());
0737                 exit(255);
0738             }
0739         }
0740     }
0741 
0742     /** Test if socket file is already present
0743      *  note that access() resolves symlinks, and so we check the actual
0744      *  socket file if it exists
0745      */
0746     if (access(sock_file, W_OK) == 0) {
0747         int s;
0748         struct sockaddr_un server;
0749 
0750 //     fprintf(stderr, "kdeinit5: Warning, socket_file already exists!\n");
0751         // create the socket stream
0752         s = socket(PF_UNIX, SOCK_STREAM, 0);
0753         if (s < 0) {
0754             perror("socket() failed");
0755             exit(255);
0756         }
0757         server.sun_family = AF_UNIX;
0758         qstrncpy(server.sun_path, sock_file, sizeof(server.sun_path));
0759         socklen = sizeof(server);
0760 
0761         if (connect(s, (struct sockaddr *)&server, socklen) == 0) {
0762             fprintf(stderr, "kdeinit5: Shutting down running client.\n");
0763             klauncher_header request_header;
0764             request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
0765             request_header.arg_length = 0;
0766             write(s, &request_header, sizeof(request_header));
0767             sleep(1); // Give it some time
0768         }
0769         close(s);
0770     }
0771 
0772     // Delete any stale socket file (and symlink)
0773     unlink(sock_file);
0774 
0775     // Create socket
0776     d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0);
0777     if (d.wrapper < 0) {
0778         perror("kdeinit5: Aborting. socket() failed");
0779         exit(255);
0780     }
0781 
0782     options = fcntl(d.wrapper, F_GETFL);
0783     if (options == -1) {
0784         perror("kdeinit5: Aborting. Can not make socket non-blocking");
0785         close(d.wrapper);
0786         exit(255);
0787     }
0788 
0789     if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1) {
0790         perror("kdeinit5: Aborting. Can not make socket non-blocking");
0791         close(d.wrapper);
0792         exit(255);
0793     }
0794 
0795     while (1) {
0796         // bind it
0797         socklen = sizeof(sa);
0798         memset(&sa, 0, socklen);
0799         sa.sun_family = AF_UNIX;
0800         qstrncpy(sa.sun_path, sock_file, sizeof(sa.sun_path));
0801         if (bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0) {
0802             if (max_tries == 0) {
0803                 perror("kdeinit5: Aborting. bind() failed");
0804                 fprintf(stderr, "Could not bind to socket '%s'\n", sock_file);
0805                 close(d.wrapper);
0806                 exit(255);
0807             }
0808             max_tries--;
0809         } else {
0810             break;
0811         }
0812     }
0813 
0814     // set permissions
0815     if (chmod(sock_file, 0600) != 0) {
0816         perror("kdeinit5: Aborting. Can not set permissions on socket");
0817         fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
0818         unlink(sock_file);
0819         close(d.wrapper);
0820         exit(255);
0821     }
0822 
0823     if (listen(d.wrapper, SOMAXCONN) < 0) {
0824         perror("kdeinit5: Aborting. listen() failed");
0825         unlink(sock_file);
0826         close(d.wrapper);
0827         exit(255);
0828     }
0829 }
0830 
0831 /*
0832  * Read 'len' bytes from 'sock' into buffer.
0833  * returns 0 on success, -1 on failure.
0834  */
0835 static int read_socket(int sock, char *buffer, int len)
0836 {
0837     ssize_t result;
0838     int bytes_left = len;
0839     while (bytes_left > 0) {
0840         result = read(sock, buffer, bytes_left);
0841         if (result > 0) {
0842             buffer += result;
0843             bytes_left -= result;
0844         } else if (result == 0) {
0845             return -1;
0846         } else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) {
0847             return -1;
0848         }
0849     }
0850     return 0;
0851 }
0852 
0853 static void start_klauncher()
0854 {
0855     if (socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher) < 0) {
0856         perror("kdeinit5: socketpair() failed");
0857         exit(255);
0858     }
0859     char args[32];
0860     strcpy(args, "--fd=");
0861     sprintf(args + 5, "%d", d.launcher[1]);
0862     d.launcher_pid = launch(2, KDE_INSTALL_FULL_LIBEXECDIR_KF5 "/klauncher", args);
0863     close(d.launcher[1]);
0864 #ifndef NDEBUG
0865     fprintf(stderr, "kdeinit5: Launched KLauncher, pid = %ld, result = %d\n",
0866             (long) d.launcher_pid, d.result);
0867 #endif
0868 }
0869 
0870 static void launcher_died()
0871 {
0872     if (!d.launcher_ok) {
0873         // This is bad.
0874         fprintf(stderr, "kdeinit5: Communication error with launcher. Exiting!\n");
0875         ::exit(255);
0876         return;
0877     }
0878 
0879     // KLauncher died... restart
0880 #ifndef NDEBUG
0881     fprintf(stderr, "kdeinit5: KLauncher died unexpectedly.\n");
0882 #endif
0883     // Make sure it's really dead.
0884     if (d.launcher_pid) {
0885         kill(d.launcher_pid, SIGKILL);
0886         sleep(1); // Give it some time
0887     }
0888 
0889     d.launcher_ok = false;
0890     d.launcher_pid = 0;
0891     close(d.launcher[0]);
0892     d.launcher[0] = -1;
0893 
0894     start_klauncher();
0895 }
0896 
0897 static bool handle_launcher_request(int sock, const char *who)
0898 {
0899     (void)who; // for NDEBUG
0900 
0901     klauncher_header request_header;
0902     char *request_data = nullptr;
0903     int result = read_socket(sock, (char *) &request_header, sizeof(request_header));
0904     if (result != 0) {
0905         return false;
0906     }
0907 
0908     if (request_header.arg_length != 0) {
0909         request_data = (char *) malloc(request_header.arg_length + 1);
0910         request_data[request_header.arg_length] = '\0';
0911 
0912         result = read_socket(sock, request_data, request_header.arg_length);
0913         if (result != 0) {
0914             free(request_data);
0915             return false;
0916         }
0917     }
0918 
0919     //qDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd);
0920     if (request_header.cmd == LAUNCHER_OK) {
0921         d.launcher_ok = true;
0922     } else if (request_header.arg_length &&
0923                ((request_header.cmd == LAUNCHER_EXEC) ||
0924                 (request_header.cmd == LAUNCHER_EXT_EXEC) ||
0925                 (request_header.cmd == LAUNCHER_SHELL) ||
0926                 (request_header.cmd == LAUNCHER_KWRAPPER) ||
0927                 (request_header.cmd == LAUNCHER_EXEC_NEW))) {
0928         pid_t pid;
0929         klauncher_header response_header;
0930         long response_data;
0931         long l;
0932         memcpy(&l, request_data, sizeof(long));
0933         int argc = l;
0934         const char *name = request_data + sizeof(long);
0935         const char *args = name + strlen(name) + 1;
0936         const char *cwd = nullptr;
0937         int envc = 0;
0938         const char *envs = nullptr;
0939         const char *tty = nullptr;
0940         int avoid_loops = 0;
0941         const char *startup_id_str = "0"; // krazy:exclude=doublequote_chars
0942 
0943 #ifndef NDEBUG
0944         fprintf(stderr, "kdeinit5: Got %s '%s' from %s.\n",
0945                 commandToString(request_header.cmd),
0946                 name, who);
0947 #endif
0948 
0949         const char *arg_n = args;
0950         for (int i = 1; i < argc; i++) {
0951             arg_n = arg_n + strlen(arg_n) + 1;
0952         }
0953 
0954         if (request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER) {
0955             // Shell or kwrapper
0956             cwd = arg_n; arg_n += strlen(cwd) + 1;
0957         }
0958         if (request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
0959                 || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW) {
0960             memcpy(&l, arg_n, sizeof(long));
0961             envc = l;
0962             arg_n += sizeof(long);
0963             envs = arg_n;
0964             for (int i = 0; i < envc; i++) {
0965                 arg_n = arg_n + strlen(arg_n) + 1;
0966             }
0967             if (request_header.cmd == LAUNCHER_KWRAPPER) {
0968                 tty = arg_n;
0969                 arg_n += strlen(tty) + 1;
0970             }
0971         }
0972 
0973         if (request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
0974                 || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW) {
0975             memcpy(&l, arg_n, sizeof(long));
0976             avoid_loops = l;
0977             arg_n += sizeof(long);
0978         }
0979 
0980         if (request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
0981                 || request_header.cmd == LAUNCHER_EXT_EXEC) {
0982             startup_id_str = arg_n;
0983             arg_n += strlen(startup_id_str) + 1;
0984         }
0985 
0986         if ((request_header.arg_length > (arg_n - request_data)) &&
0987                 (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW)) {
0988             // Optional cwd
0989             cwd = arg_n; arg_n += strlen(cwd) + 1;
0990         }
0991 
0992         if ((arg_n - request_data) != request_header.arg_length) {
0993 #ifndef NDEBUG
0994             fprintf(stderr, "kdeinit5: EXEC request has invalid format.\n");
0995 #endif
0996             free(request_data);
0997             d.debug_wait = false;
0998             return true; // sure?
0999         }
1000 
1001 #if HAVE_X11 || HAVE_XCB
1002         // support for the old a bit broken way of setting DISPLAY for multihead
1003         QByteArray olddisplay = qgetenv(displayEnvVarName_c());
1004         QByteArray kdedisplay = qgetenv("KDE_DISPLAY");
1005         bool reset_display = (! olddisplay.isEmpty() &&
1006                               ! kdedisplay.isEmpty() &&
1007                               olddisplay != kdedisplay);
1008 
1009         if (reset_display) {
1010             qputenv(displayEnvVarName_c(), kdedisplay);
1011         }
1012 #endif
1013         pid = launch(argc, name, args, cwd, envc, envs,
1014                      request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER,
1015                      tty, avoid_loops, startup_id_str);
1016 
1017 #if HAVE_X11 || HAVE_XCB
1018         if (reset_display) {
1019             unsetenv("KDE_DISPLAY");
1020             qputenv(displayEnvVarName_c(), olddisplay);
1021         }
1022 #endif
1023 
1024         if (pid && (d.result == 0)) {
1025             response_header.cmd = LAUNCHER_OK;
1026             response_header.arg_length = sizeof(response_data);
1027             response_data = pid;
1028             write(sock, &response_header, sizeof(response_header));
1029             write(sock, &response_data, response_header.arg_length);
1030 
1031             // add new child to list
1032             struct child *child = (struct child *) malloc(sizeof(struct child));
1033             child->pid = pid;
1034             child->sock = dup(sock);
1035             child->next = children;
1036             children = child;
1037         } else {
1038             int l = d.errorMsg.length();
1039             if (l) {
1040                 l++;    // Include trailing null.
1041             }
1042             response_header.cmd = LAUNCHER_ERROR;
1043             response_header.arg_length = l;
1044             write(sock, &response_header, sizeof(response_header));
1045             if (l) {
1046                 write(sock, d.errorMsg.data(), l);
1047             }
1048         }
1049         d.debug_wait = false;
1050     } else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV) {
1051         const char *env_name;
1052         const char *env_value;
1053         env_name = request_data;
1054         env_value = env_name + strlen(env_name) + 1;
1055 
1056 #ifndef NDEBUG
1057         fprintf(stderr, "kdeinit5: Got SETENV '%s=%s' from %s.\n", env_name, env_value, who);
1058 #endif
1059 
1060         if (request_header.arg_length !=
1061                 (int)(strlen(env_name) + strlen(env_value) + 2)) {
1062 #ifndef NDEBUG
1063             fprintf(stderr, "kdeinit5: SETENV request has invalid format.\n");
1064 #endif
1065             free(request_data);
1066             return true; // sure?
1067         }
1068         qputenv(env_name, env_value);
1069     } else if (request_header.cmd == LAUNCHER_TERMINATE_KDE) {
1070 #ifndef NDEBUG
1071         fprintf(stderr, "kdeinit5: terminate KDE.\n");
1072 #endif
1073 #if HAVE_X11
1074         kdeinit_xio_errhandler(nullptr);
1075 #endif
1076     } else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT) {
1077 #ifndef NDEBUG
1078         fprintf(stderr, "kdeinit5: Got termination request (PID %ld).\n", (long) getpid());
1079 #endif
1080         if (d.launcher_pid) {
1081             kill(d.launcher_pid, SIGTERM);
1082             d.launcher_pid = 0;
1083             close(d.launcher[0]);
1084             d.launcher[0] = -1;
1085         }
1086         unlink(sock_file);
1087         if (children) {
1088             close(d.wrapper);
1089             d.wrapper = -1;
1090 #ifndef NDEBUG
1091             fprintf(stderr, "kdeinit5: Closed sockets, but not exiting until all children terminate.\n");
1092 #endif
1093         } else {
1094             raise(SIGTERM);
1095         }
1096     } else if (request_header.cmd == LAUNCHER_DEBUG_WAIT) {
1097 #ifndef NDEBUG
1098         fprintf(stderr, "kdeinit5: Debug wait activated.\n");
1099 #endif
1100         d.debug_wait = true;
1101     }
1102     if (request_data) {
1103         free(request_data);
1104     }
1105     return true;
1106 }
1107 
1108 static void handle_requests(pid_t waitForPid)
1109 {
1110     int max_sock = d.deadpipe[0];
1111     if (d.wrapper > max_sock) {
1112         max_sock = d.wrapper;
1113     }
1114     if (d.launcher[0] > max_sock) {
1115         max_sock = d.launcher[0];
1116     }
1117 #if HAVE_X11
1118     if (X11fd > max_sock) {
1119         max_sock = X11fd;
1120     }
1121 #endif
1122     max_sock++;
1123 
1124     while (1) {
1125         fd_set rd_set;
1126         fd_set wr_set;
1127         fd_set e_set;
1128         int result;
1129         pid_t exit_pid;
1130         int exit_status;
1131         char c;
1132 
1133         // Flush the pipe of death
1134         while (read(d.deadpipe[0], &c, 1) == 1) {
1135         }
1136 
1137         // Handle dying children
1138         do {
1139             exit_pid = waitpid(-1, &exit_status, WNOHANG);
1140             if (exit_pid > 0) {
1141 #ifndef NDEBUG
1142                 fprintf(stderr, "kdeinit5: PID %ld terminated.\n", (long) exit_pid);
1143 #endif
1144                 if (waitForPid && (exit_pid == waitForPid)) {
1145                     return;
1146                 }
1147 
1148                 if (WIFEXITED(exit_status)) { // fix process return value
1149                     exit_status = WEXITSTATUS(exit_status);
1150                 } else if (WIFSIGNALED(exit_status)) {
1151                     exit_status = 128 + WTERMSIG(exit_status);
1152                 }
1153                 child_died(exit_pid, exit_status);
1154 
1155                 if (d.wrapper < 0 && !children) {
1156 #ifndef NDEBUG
1157                     fprintf(stderr, "kdeinit5: Last child terminated, exiting (PID %ld).\n",
1158                             (long) getpid());
1159 #endif
1160                     raise(SIGTERM);
1161                 }
1162             }
1163         } while (exit_pid > 0);
1164 
1165         FD_ZERO(&rd_set);
1166         FD_ZERO(&wr_set);
1167         FD_ZERO(&e_set);
1168 
1169         if (d.launcher[0] >= 0) {
1170             FD_SET(d.launcher[0], &rd_set);
1171         }
1172         if (d.wrapper >= 0) {
1173             FD_SET(d.wrapper, &rd_set);
1174         }
1175         FD_SET(d.deadpipe[0], &rd_set);
1176 #if HAVE_X11
1177         if (X11fd >= 0) {
1178             FD_SET(X11fd, &rd_set);
1179         }
1180 #endif
1181 
1182         result = select(max_sock, &rd_set, &wr_set, &e_set, nullptr);
1183         if (result < 0) {
1184             if (errno == EINTR || errno == EAGAIN) {
1185                 continue;
1186             }
1187             perror("kdeinit5: Aborting. select() failed");
1188             return;
1189         }
1190 
1191         // Handle wrapper request
1192         if (d.wrapper >= 0 && FD_ISSET(d.wrapper, &rd_set)) {
1193             struct sockaddr_un client;
1194             kde_socklen_t sClient = sizeof(client);
1195             int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient);
1196             if (sock >= 0) {
1197                 d.accepted_fd = sock;
1198                 handle_launcher_request(sock, "wrapper");
1199                 close(sock);
1200                 d.accepted_fd = -1;
1201             }
1202         }
1203 
1204         // Handle launcher request
1205         if (d.launcher[0] >= 0 && FD_ISSET(d.launcher[0], &rd_set)) {
1206             if (!handle_launcher_request(d.launcher[0], "launcher")) {
1207                 launcher_died();
1208             }
1209             if (waitForPid == d.launcher_pid) {
1210                 return;
1211             }
1212         }
1213 
1214 #if HAVE_X11
1215         // Look for incoming X11 events
1216         if (X11fd >= 0 && FD_ISSET(X11fd, &rd_set)) {
1217             if (X11display != nullptr) {
1218                 XEvent event_return;
1219                 while (XPending(X11display)) {
1220                     XNextEvent(X11display, &event_return);
1221                 }
1222             }
1223         }
1224 #endif
1225     }
1226 }
1227 
1228 static void generate_socket_name()
1229 {
1230 #if HAVE_X11 || HAVE_XCB
1231     QByteArray display = qgetenv(displayEnvVarName_c());
1232     if (display.isEmpty()) {
1233         fprintf(stderr, "kdeinit5: Aborting. $%s is not set. \n", displayEnvVarName_c());
1234         exit(255);
1235     }
1236     int i;
1237     if ((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) {
1238         display.truncate(i);
1239     }
1240 
1241     display.replace(':', '_');
1242 #ifdef __APPLE__
1243     // not entirely impossible, so let's leave it
1244     display.replace('/', '_');
1245 #endif
1246 #else
1247     // not using a DISPLAY variable; use an empty string instead
1248     QByteArray display = "";
1249 #endif
1250     // WARNING, if you change the socket name, adjust kwrapper too
1251     const QString socketFileName = QStringLiteral("kdeinit5_%1").arg(QLatin1String(display));
1252     const QByteArray socketName = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QLatin1Char('/') + socketFileName);
1253     if (static_cast<unsigned>(socketName.length()) >= MAX_SOCK_FILE) {
1254         fprintf(stderr, "kdeinit5: Aborting. Socket name will be too long:\n");
1255         fprintf(stderr, "         '%s'\n", socketName.data());
1256         exit(255);
1257     }
1258     strcpy(sock_file, socketName.data());
1259 }
1260 
1261 #if HAVE_X11
1262 int kdeinit_xio_errhandler(Display *disp)
1263 {
1264     // disp is nullptr when KDE shuts down. We don't want those warnings then.
1265 
1266     if (disp) {
1267         qWarning("kdeinit5: Fatal IO error: client killed");
1268     }
1269 
1270     if (sock_file[0]) {
1271         // Delete any stale socket file
1272         unlink(sock_file);
1273     }
1274 
1275     // Don't kill our children in suicide mode, they may still be in use
1276     if (d.suicide) {
1277         if (d.launcher_pid) {
1278             kill(d.launcher_pid, SIGTERM);
1279         }
1280         if (d.kded_pid) {
1281             kill(d.kded_pid, SIGTERM);
1282         }
1283         exit(0);
1284     }
1285 
1286     if (disp) {
1287         qWarning("kdeinit5: sending SIGHUP to children.");
1288     }
1289 
1290     // this should remove all children we started
1291     signal(SIGHUP, SIG_IGN);
1292     kill(0, SIGHUP);
1293 
1294     sleep(2);
1295 
1296     if (disp) {
1297         qWarning("kdeinit5: sending SIGTERM to children.");
1298     }
1299 
1300     // and if they don't listen to us, this should work
1301     signal(SIGTERM, SIG_IGN);
1302     kill(0, SIGTERM);
1303 
1304     if (disp) {
1305         qWarning("kdeinit5: Exit.");
1306     }
1307 
1308     exit(0);
1309     return 0;
1310 }
1311 
1312 int kdeinit_x_errhandler(Display *dpy, XErrorEvent *err)
1313 {
1314 #ifndef NDEBUG
1315     char errstr[256];
1316     // kdeinit almost doesn't use X, and therefore there shouldn't be any X error
1317     XGetErrorText(dpy, err->error_code, errstr, 256);
1318     fprintf(stderr, "kdeinit5(%d) : KDE detected X Error: %s %d\n"
1319             "         Major opcode: %d\n"
1320             "         Minor opcode: %d\n"
1321             "         Resource id:  0x%lx\n",
1322             getpid(), errstr, err->error_code, err->request_code, err->minor_code, err->resourceid);
1323 
1324 #else
1325     Q_UNUSED(dpy);
1326     Q_UNUSED(err);
1327 #endif
1328     return 0;
1329 }
1330 
1331 // needs to be done sooner than initXconnection() because of also opening
1332 // another X connection for startup notification purposes
1333 static void setupX()
1334 {
1335     XSetIOErrorHandler(kdeinit_xio_errhandler);
1336     XSetErrorHandler(kdeinit_x_errhandler);
1337     /*
1338         Handle the tricky case of running via kdesu/su/sudo/etc. There the usual case
1339         is that kdesu (etc.) creates a file with xauth information, sets XAUTHORITY,
1340         runs the command and removes the xauth file after the command finishes. However,
1341         dbus and kdeinit daemon currently don't clean up properly and keeping running.
1342         Which means that running a KDE app via kdesu the second time talks to kdeinit
1343         with obsolete xauth information, which makes it unable to connect to X or launch
1344         any X11 applications.
1345         Even fixing the cleanup probably wouldn't be sufficient, since it'd be possible to
1346         launch one kdesu session, another one, exit the first one and the app from the second
1347         session would be using kdeinit from the first one.
1348         So the trick here is to duplicate the xauth file to another file in KDE's tmp
1349         location, make the file have a consistent name so that future sessions will use it
1350         as well, point XAUTHORITY there and never remove the file (except for possible
1351         tmp cleanup).
1352     */
1353     if (!qEnvironmentVariableIsEmpty("XAUTHORITY")) {
1354         QByteArray display = qgetenv(displayEnvVarName_c());
1355         int i;
1356         if ((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) {
1357             display.truncate(i);
1358         }
1359         display.replace(':', '_');
1360 #ifdef __APPLE__
1361         display.replace('/', '_');
1362 #endif
1363         QString xauth = QDir::tempPath() + QLatin1String("/xauth-")
1364                         + QString::number(getuid()) + QLatin1Char('-') + QString::fromLocal8Bit(display);
1365         QSaveFile xauthfile(xauth);
1366         QFile xauthfrom(QFile::decodeName(qgetenv("XAUTHORITY")));
1367         // Set umask to make sure the file permissions of xauthfile are correct
1368         mode_t oldMask = umask(S_IRGRP | S_IROTH | S_IWGRP | S_IWOTH);
1369         if (!xauthfrom.open(QFile::ReadOnly) || !xauthfile.open(QFile::WriteOnly)
1370                 || xauthfile.write(xauthfrom.readAll()) != xauthfrom.size() || !xauthfile.commit()) {
1371             // error
1372         } else {
1373             qputenv("XAUTHORITY", QFile::encodeName(xauth));
1374         }
1375         umask(oldMask);
1376     }
1377 }
1378 
1379 static int initXconnection()
1380 {
1381     X11display = XOpenDisplay(nullptr);
1382     if (X11display != nullptr) {
1383         XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0, 0, 1, 1, \
1384                             0,
1385                             BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)),
1386                             BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)));
1387 #ifndef NDEBUG
1388         fprintf(stderr, "kdeinit5: opened connection to %s\n", DisplayString(X11display));
1389 #endif
1390         int fd = XConnectionNumber(X11display);
1391         int on = 1;
1392         (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on));
1393         return fd;
1394     } else
1395         fprintf(stderr, "kdeinit5: Can not connect to the X Server.\n" \
1396                 "kdeinit5: Might not terminate at end of session.\n");
1397 
1398     return -1;
1399 }
1400 #endif
1401 
1402 #ifndef Q_OS_OSX
1403 // Find a shared lib in the lib dir, e.g. libkio.so.
1404 // Completely unrelated to plugins.
1405 static QString findSharedLib(const QString &lib)
1406 {
1407     QString path = QFile::decodeName(CMAKE_INSTALL_PREFIX "/" KDE_INSTALL_LIBDIR "/") + lib;
1408     if (QFile::exists(path)) {
1409         return path;
1410     }
1411     // We could also look in LD_LIBRARY_PATH, but really, who installs the main libs in different prefixes?
1412     return QString();
1413 }
1414 #endif
1415 
1416 extern "C" {
1417 
1418     static void secondary_child_handler(int)
1419     {
1420         waitpid(-1, nullptr, WNOHANG);
1421     }
1422 
1423 }
1424 
1425 int main(int argc, char **argv)
1426 {
1427 #ifndef _WIN32_WCE
1428     setlocale(LC_ALL, "");
1429     setlocale(LC_NUMERIC, "C");
1430 #endif
1431 
1432     pid_t pid;
1433     bool do_fork = true;
1434     int launch_klauncher = 1;
1435     int launch_kded = 0;
1436     int keep_running = 1;
1437     d.suicide = false;
1438 
1439     // Save arguments first...
1440     char **safe_argv = (char **) malloc(sizeof(char *) * argc);
1441     for (int i = 0; i < argc; i++) {
1442         safe_argv[i] = strcpy((char *)malloc(strlen(argv[i]) + 1), argv[i]);
1443         if (strcmp(safe_argv[i], "--no-klauncher") == 0) {
1444             launch_klauncher = 0;
1445         }
1446         if (strcmp(safe_argv[i], "--kded") == 0) {
1447             launch_kded = 1;
1448         }
1449         // allow both nofork and no-fork for compatibility with
1450         // old versions (both of this and of KUniqueApplication)
1451         if (strcmp(safe_argv[i], "--nofork") == 0) {
1452             do_fork = false;
1453         }
1454         if (strcmp(safe_argv[i], "--no-fork") == 0) {
1455             do_fork = false;
1456         }
1457         if (strcmp(safe_argv[i], "--suicide") == 0) {
1458             d.suicide = true;
1459         }
1460         if (strcmp(safe_argv[i], "--exit") == 0) {
1461             keep_running = 0;
1462         }
1463         if (strcmp(safe_argv[i], "--version") == 0) {
1464             printf("Qt: %s\n", qVersion());
1465             printf("KDE: %s\n", KINIT_VERSION_STRING);
1466             exit(0);
1467         }
1468 #if KDEINIT_OOM_PROTECT
1469         if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i + 1 < argc) {
1470             oom_pipe = atol(argv[i + 1]);
1471         }
1472 #endif
1473         if (strcmp(safe_argv[i], "--help") == 0) {
1474             printf("Usage: kdeinit5 [options]\n");
1475             printf("    --no-fork         Do not fork\n");
1476             // printf("    --no-klauncher    Do not start klauncher\n");
1477             printf("    --kded            Start kded\n");
1478             printf("    --suicide         Terminate when no KDE applications are left running\n");
1479             printf("    --version         Show version information\n");
1480             // printf("    --exit            Terminate when kded has run\n");
1481             exit(0);
1482         }
1483     }
1484 
1485     cleanup_fds();
1486 
1487     // Redirect stdout to stderr. We have no reason to use stdout anyway.
1488     // This minimizes our impact on commands used in pipes.
1489     (void)dup2(2, 1);
1490 
1491     if (do_fork) {
1492 #ifdef Q_OS_OSX
1493         mac_fork_and_reexec_self();
1494 #else
1495         if (pipe(d.initpipe) != 0) {
1496             perror("kdeinit5: pipe failed");
1497             return 1;
1498         }
1499 
1500         // Fork here and let parent process exit.
1501         // Parent process may only exit after all required services have been
1502         // launched. (dcopserver/klauncher and services which start with '+')
1503         signal(SIGCHLD, secondary_child_handler);
1504         if (fork() > 0) { // Go into background
1505             close(d.initpipe[1]);
1506             d.initpipe[1] = -1;
1507             // wait till init is complete
1508             char c;
1509             while (read(d.initpipe[0], &c, 1) < 0)
1510                 ;
1511             // then exit;
1512             close(d.initpipe[0]);
1513             d.initpipe[0] = -1;
1514             return 0;
1515         }
1516         close(d.initpipe[0]);
1517         d.initpipe[0] = -1;
1518 #endif
1519     }
1520 
1521     // Make process group leader (for shutting down children later)
1522     if (keep_running) {
1523         setsid();
1524     }
1525 
1526     // Prepare to change process name
1527 #ifndef SKIP_PROCTITLE
1528     proctitle_init(argc, argv);
1529 #endif
1530 
1531     // don't change envvars before proctitle_init()
1532     unsetenv("LD_BIND_NOW");
1533     unsetenv("DYLD_BIND_AT_LAUNCH");
1534     KCrash::loadedByKdeinit = true;
1535 
1536     d.maxname = strlen(argv[0]);
1537     d.launcher_pid = 0;
1538     d.kded_pid = 0;
1539     d.wrapper = -1;
1540     d.accepted_fd = -1;
1541     d.debug_wait = false;
1542     d.launcher_ok = false;
1543     children = nullptr;
1544     init_signals();
1545 #if HAVE_X11
1546     setupX();
1547 #endif
1548 
1549     generate_socket_name();
1550     if (keep_running) {
1551         // Create <runtime dir>/kdeinit5-<display> socket for incoming wrapper requests.
1552         init_kdeinit_socket();
1553     }
1554 #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
1555     if (!d.suicide && qEnvironmentVariableIsEmpty("KDE_IS_PRELINKED")) {
1556         for (const char *extra_lib : extra_libs) {
1557             const QString extra = findSharedLib(QString::fromLatin1(extra_lib));
1558             if (!extra.isEmpty()) {
1559                 QLibrary l(extra);
1560                 l.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1561                 (void)l.load();
1562             }
1563 #ifndef NDEBUG
1564             else {
1565                 fprintf(stderr, "%s was not found.\n", extra_lib);
1566             }
1567 #endif
1568 
1569         }
1570     }
1571 #endif
1572     if (launch_klauncher) {
1573         start_klauncher();
1574         handle_requests(d.launcher_pid); // Wait for klauncher to be ready
1575     }
1576 
1577 #if HAVE_X11
1578     X11fd = initXconnection();
1579 #endif
1580 
1581     QFont::initialize();
1582 #if HAVE_X11
1583     if (XSupportsLocale()) {
1584         // Similar to QApplication::create_xim()
1585         // but we need to use our own display
1586         XOpenIM(X11display, nullptr, nullptr, nullptr);
1587     }
1588 #endif
1589 
1590     if (launch_kded) {
1591         qputenv("KDED_STARTED_BY_KDEINIT", "1");
1592         pid = launch(1, KDED_EXENAME, nullptr);
1593         unsetenv("KDED_STARTED_BY_KDEINIT");
1594 #ifndef NDEBUG
1595         fprintf(stderr, "kdeinit5: Launched KDED, pid = %ld result = %d\n", (long) pid, d.result);
1596 #endif
1597         d.kded_pid = pid;
1598         // kded5 doesn't fork on startup anymore, so just move on.
1599         //handle_requests(pid);
1600     }
1601 
1602     for (int i = 1; i < argc; i++) {
1603         if (safe_argv[i][0] == '+') {
1604             pid = launch(1, safe_argv[i] + 1, nullptr);
1605 #ifndef NDEBUG
1606             fprintf(stderr, "kdeinit5: Launched '%s', pid = %ld result = %d\n", safe_argv[i] + 1, (long) pid, d.result);
1607 #endif
1608             handle_requests(pid);
1609         } else if (safe_argv[i][0] == '-'
1610 #if KDEINIT_OOM_PROTECT
1611                    || isdigit(safe_argv[i][0])
1612 #endif
1613                   ) {
1614             // Ignore
1615         } else {
1616             pid = launch(1, safe_argv[i], nullptr);
1617 #ifndef NDEBUG
1618             fprintf(stderr, "kdeinit5: Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result);
1619 #endif
1620         }
1621     }
1622 
1623     // Free arguments
1624     for (int i = 0; i < argc; i++) {
1625         free(safe_argv[i]);
1626     }
1627     free(safe_argv);
1628 
1629 #ifndef SKIP_PROCTITLE
1630     proctitle_set("Running...");
1631 #endif
1632 
1633     if (!keep_running) {
1634         return 0;
1635     }
1636 
1637     if (d.initpipe[1] != -1) {
1638         char c = 0;
1639         write(d.initpipe[1], &c, 1); // Kdeinit is started.
1640         close(d.initpipe[1]);
1641         d.initpipe[1] = -1;
1642     }
1643 
1644     handle_requests(0);
1645 
1646     return 0;
1647 }