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 }