File indexing completed on 2024-04-21 14:59:31
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999-2000 Waldo Bastian <bastian@kde.org> 0004 SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@sime.com> 0005 SPDX-FileCopyrightText: 2001 Lubos Lunak <l.lunak@kde.org> 0006 SPDX-FileCopyrightText: 2006-2011 Ralf Habacker <ralf.habacker@freenet.de> 0007 SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de> 0008 0009 SPDX-License-Identifier: LGPL-2.0-only 0010 */ 0011 0012 // Get this to compile... TODO: fix properly 0013 #undef QT_NO_CAST_FROM_ASCII 0014 0015 #include <cerrno> 0016 #include <stdio.h> 0017 #include <stdlib.h> 0018 #include <string.h> 0019 0020 #include <windows.h> 0021 #ifndef _WIN32_WCE 0022 #include <sddl.h> 0023 #endif 0024 #include <tlhelp32.h> 0025 #include <psapi.h> 0026 0027 #include <QProcess> 0028 #include <QFileInfo> 0029 #include <QDebug> 0030 0031 // Under wince interface is defined, so undef it otherwise it breaks it 0032 #undef interface 0033 #include <QDBusConnection> 0034 #include <QDBusInterface> 0035 #include <QDBusConnectionInterface> 0036 0037 #include <kinit_version.h> 0038 0039 #if defined (Q_CC_MSVC) 0040 typedef unsigned int pid_t; 0041 #else 0042 #include <sys/types.h> 0043 #endif 0044 0045 //#define ENABLE_SUICIDE 0046 //#define ENABLE_EXIT 0047 0048 #define KDED_EXENAME "kded5" 0049 0050 // print verbose messages 0051 int verbose = 0; 0052 0053 /// holds process list for suicide mode 0054 QList<QProcess *> startedProcesses; 0055 0056 /* -------------------------------------------------------------------- 0057 sid helper - will be migrated later to a class named Sid, which could 0058 be used as base class for platform independent K_UID and K_GID types 0059 - would this be possible before KDE 5 ? 0060 --------------------------------------------------------------------- */ 0061 0062 /** 0063 copy sid 0064 @param from sif to copy from 0065 @return copied sid, need to be free'd with free 0066 @note null sid's are handled too 0067 */ 0068 PSID copySid(PSID from) 0069 { 0070 if (!from) { 0071 return 0; 0072 } 0073 int sidLength = GetLengthSid(from); 0074 PSID to = (PSID) malloc(sidLength); 0075 CopySid(sidLength, to, from); 0076 return to; 0077 } 0078 0079 /** 0080 copy sid 0081 @param from sif to copy from 0082 @return copied sid, need to be free'd with free 0083 @note null sid's are handled too 0084 */ 0085 void freeSid(PSID sid) 0086 { 0087 if (sid) { 0088 free(sid); 0089 } 0090 } 0091 0092 /** 0093 copy sid 0094 @param from sif to copy from 0095 @return copied sid, need to be free'd with free 0096 @note null sid's are handled too 0097 */ 0098 QString toString(PSID sid) 0099 { 0100 LPWSTR s; 0101 if (!ConvertSidToStringSid(sid, &s)) { 0102 return QString(); 0103 } 0104 0105 QString result = QString::fromUtf16(reinterpret_cast<ushort *>(s)); 0106 LocalFree(s); 0107 return result; 0108 } 0109 0110 /* -------------------------------------------------------------------- 0111 process helper 0112 --------------------------------------------------------------------- */ 0113 0114 /** 0115 return process handle 0116 @param pid process id 0117 @return process handle 0118 */ 0119 static HANDLE getProcessHandle(int processID) 0120 { 0121 return OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | 0122 PROCESS_VM_READ | PROCESS_TERMINATE, 0123 false, processID); 0124 } 0125 0126 /** 0127 return absolute path of process 0128 @param pid process id 0129 @return process name 0130 */ 0131 static QString getProcessName(DWORD pid) 0132 { 0133 HANDLE hModuleSnap = INVALID_HANDLE_VALUE; 0134 MODULEENTRY32 me32; 0135 0136 hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); 0137 if (hModuleSnap == INVALID_HANDLE_VALUE) { 0138 return QString(); 0139 } 0140 0141 me32.dwSize = sizeof(MODULEENTRY32); 0142 0143 if (!Module32First(hModuleSnap, &me32)) { 0144 CloseHandle(hModuleSnap); // clean the snapshot object 0145 return QString(); 0146 } 0147 QString name = QString::fromWCharArray(me32.szExePath); 0148 CloseHandle(hModuleSnap); 0149 return name; 0150 } 0151 0152 /** 0153 return sid of specific process 0154 @param hProcess handle to process 0155 @return sid pointer to PSID structure, must be freed with LocalAlloc 0156 */ 0157 static PSID getProcessOwner(HANDLE hProcess) 0158 { 0159 #ifndef _WIN32_WCE 0160 HANDLE hToken = NULL; 0161 PSID sid; 0162 0163 OpenProcessToken(hProcess, TOKEN_READ, &hToken); 0164 if (hToken) { 0165 DWORD size; 0166 PTOKEN_USER userStruct; 0167 0168 // check how much space is needed 0169 GetTokenInformation(hToken, TokenUser, NULL, 0, &size); 0170 if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { 0171 userStruct = reinterpret_cast<PTOKEN_USER>(new BYTE[size]); 0172 GetTokenInformation(hToken, TokenUser, userStruct, size, &size); 0173 0174 sid = copySid(userStruct->User.Sid); 0175 CloseHandle(hToken); 0176 delete [] userStruct; 0177 return sid; 0178 } 0179 } 0180 #endif 0181 return 0; 0182 } 0183 0184 /** 0185 return sid of current process owner 0186 */ 0187 static PSID getCurrentProcessOwner() 0188 { 0189 return getProcessOwner(GetCurrentProcess()); 0190 } 0191 0192 /** 0193 holds single process 0194 */ 0195 class ProcessListEntry 0196 { 0197 public: 0198 ProcessListEntry(HANDLE _handle, QString _path, int _pid, PSID _owner = 0) 0199 { 0200 QFileInfo p(_path); 0201 path = p.absolutePath(); 0202 name = p.baseName(); 0203 handle = _handle; 0204 pid = _pid; 0205 owner = copySid(_owner); 0206 } 0207 0208 ~ProcessListEntry() 0209 { 0210 freeSid(owner); 0211 CloseHandle(handle); 0212 } 0213 0214 QString name; 0215 QString path; 0216 int pid; 0217 HANDLE handle; 0218 PSID owner; 0219 friend QDebug operator <<(QDebug out, const ProcessListEntry &c); 0220 }; 0221 0222 QDebug operator <<(QDebug out, const ProcessListEntry &c) 0223 { 0224 out << "(ProcessListEntry" 0225 << "name" << c.name 0226 << "path" << c.path 0227 << "pid" << c.pid 0228 << "handle" << c.handle 0229 << "sid" << toString(c.owner) 0230 << ")"; 0231 return out; 0232 } 0233 0234 /** 0235 holds system process list snapshot 0236 0237 Could be used as a public platform independent class or namespace in kdecore 0238 for dealing with system processes, named perhaps KSystemProcessSnapshot or similar. 0239 If implemented at Qt level it will be named QSystemProcessSnapshot or similar 0240 */ 0241 class ProcessList 0242 { 0243 public: 0244 /** 0245 collect process 0246 @param userSid sid of user for which processes should be collected or 0 for all processes 0247 */ 0248 ProcessList(PSID userSid = 0); 0249 0250 ~ProcessList(); 0251 0252 /** 0253 find process in list 0254 @param name process name (with or without extension) 0255 @return instance of process entry 0256 */ 0257 ProcessListEntry *find(const QString &name); 0258 0259 /** 0260 killprocess from list 0261 @param name process name (with or without extension) 0262 @return ... 0263 */ 0264 bool terminateProcess(const QString &name); 0265 0266 /** 0267 return all processes 0268 @return list with processes 0269 */ 0270 QList<ProcessListEntry *> &list() 0271 { 0272 return m_processes; 0273 } 0274 0275 private: 0276 void init(); 0277 QList<ProcessListEntry *> m_processes; 0278 PSID m_userId; 0279 }; 0280 0281 ProcessList::ProcessList(PSID userSid) 0282 { 0283 m_userId = userSid; 0284 init(); 0285 } 0286 0287 ProcessList::~ProcessList() 0288 { 0289 qDeleteAll(m_processes); 0290 } 0291 0292 void ProcessList::init() 0293 { 0294 HANDLE h; 0295 PROCESSENTRY32 pe32; 0296 0297 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 0298 if (h == INVALID_HANDLE_VALUE) { 0299 return; 0300 } 0301 pe32.dwSize = sizeof(PROCESSENTRY32); 0302 if (!Process32First(h, &pe32)) { 0303 return; 0304 } 0305 0306 do { 0307 HANDLE hProcess = getProcessHandle(pe32.th32ProcessID); 0308 if (!hProcess) { 0309 continue; 0310 } 0311 QString name = getProcessName(pe32.th32ProcessID); 0312 #ifndef _WIN32_WCE 0313 PSID sid = getProcessOwner(hProcess); 0314 if (!sid || m_userId && !EqualSid(m_userId, sid)) { 0315 freeSid(sid); 0316 continue; 0317 } 0318 #else 0319 PSID sid = 0; 0320 #endif 0321 m_processes << new ProcessListEntry(hProcess, name, pe32.th32ProcessID, sid); 0322 } while (Process32Next(h, &pe32)); 0323 #ifndef _WIN32_WCE 0324 CloseHandle(h); 0325 #else 0326 CloseToolhelp32Snapshot(h); 0327 #endif 0328 } 0329 0330 ProcessListEntry *ProcessList::find(const QString &name) 0331 { 0332 for (ProcessListEntry *ple : std::as_const(m_processes)) { 0333 if (ple->pid < 0) { 0334 qDebug() << "negative pid!"; 0335 continue; 0336 } 0337 0338 if (ple->name != name && ple->name != name + ".exe") { 0339 continue; 0340 } 0341 0342 if (!ple->path.isEmpty() && !ple->path.toLower().startsWith(QString(QStringLiteral(CMAKE_INSTALL_PREFIX)).toLower())) { 0343 // process is outside of installation directory 0344 qDebug() << "path of the process" << name << "seems to be outside of the installPath:" << ple->path << QStringLiteral(CMAKE_INSTALL_PREFIX); 0345 continue; 0346 } 0347 return ple; 0348 } 0349 return NULL; 0350 } 0351 0352 bool ProcessList::terminateProcess(const QString &name) 0353 { 0354 qDebug() << "going to terminate process" << name; 0355 ProcessListEntry *p = find(name); 0356 if (!p) { 0357 qDebug() << "could not find ProcessListEntry for process name" << name; 0358 return false; 0359 } 0360 0361 bool ret = TerminateProcess(p->handle, 0); 0362 if (ret) { 0363 int i = m_processes.indexOf(p); 0364 if (i != -1) { 0365 m_processes.removeAt(i); 0366 } 0367 delete p; 0368 return true; 0369 } else { 0370 return false; 0371 } 0372 } 0373 0374 // internal launch function 0375 int launch(const QString &cmd) 0376 { 0377 QProcess *proc = new QProcess(); 0378 proc->start(cmd, QStringList()); 0379 proc->waitForStarted(); 0380 startedProcesses << proc; 0381 int pid = proc->processId(); 0382 if (verbose) { 0383 fprintf(stderr, "%s", proc->readAllStandardError().constData()); 0384 fprintf(stderr, "%s", proc->readAllStandardOutput().constData()); 0385 } 0386 if (pid) { 0387 if (verbose) { 0388 fprintf(stderr, "kdeinit5: Launched %s, pid = %ld\n", qPrintable(cmd), (long) pid); 0389 } 0390 } else { 0391 if (verbose) { 0392 fprintf(stderr, "kdeinit5: could not launch %s, exiting\n", qPrintable(cmd)); 0393 } 0394 } 0395 return pid; 0396 } 0397 0398 /// check dbus registration 0399 bool checkIfRegisteredInDBus(const QString &name, int _timeout = 10) 0400 { 0401 int timeout = _timeout * 5; 0402 while (timeout) { 0403 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(name)) { 0404 break; 0405 } 0406 Sleep(200); 0407 timeout--; 0408 } 0409 if (!timeout) { 0410 if (verbose) { 0411 fprintf(stderr, "not registered %s in dbus after %d secs\n", qPrintable(name), _timeout); 0412 } 0413 return false; 0414 } 0415 if (verbose) { 0416 fprintf(stderr, "%s is registered in dbus\n", qPrintable(name)); 0417 } 0418 return true; 0419 } 0420 0421 void listAllRunningKDEProcesses(ProcessList &processList) 0422 { 0423 QString installPrefix = QStringLiteral(CMAKE_INSTALL_PREFIX); 0424 0425 const auto list = processList.list(); 0426 for (const ProcessListEntry *ple : list) { 0427 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower())) { 0428 fprintf(stderr, "path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid); 0429 } 0430 } 0431 } 0432 0433 void terminateAllRunningKDEProcesses(ProcessList &processList) 0434 { 0435 QString installPrefix = QStringLiteral(CMAKE_INSTALL_PREFIX); 0436 0437 const auto list = processList.list(); 0438 for (const ProcessListEntry *ple : list) { 0439 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower())) { 0440 if (verbose) { 0441 fprintf(stderr, "terminating path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid); 0442 } 0443 processList.terminateProcess(ple->name); 0444 } 0445 } 0446 } 0447 0448 void listAllNamedAppsInDBus() 0449 { 0450 QDBusConnection connection = QDBusConnection::sessionBus(); 0451 QDBusConnectionInterface *bus = connection.interface(); 0452 const QStringList services = bus->registeredServiceNames(); 0453 for (const QString &service : services) { 0454 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':'))) { 0455 continue; 0456 } 0457 fprintf(stderr, "%s \n", service.toLatin1().data()); 0458 } 0459 } 0460 0461 void quitApplicationsOverDBus() 0462 { 0463 QDBusConnection connection = QDBusConnection::sessionBus(); 0464 QDBusConnectionInterface *bus = connection.interface(); 0465 const QStringList services = bus->registeredServiceNames(); 0466 for (const QString &service : services) { 0467 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':'))) { 0468 continue; 0469 } 0470 QDBusInterface *iface = new QDBusInterface(service, 0471 QLatin1String("/MainApplication"), 0472 QLatin1String("org.kde.KApplication"), 0473 connection); 0474 if (!iface->isValid()) { 0475 if (verbose) { 0476 fprintf(stderr, "invalid interface of service %s\n", service.toLatin1().data()); 0477 } 0478 continue; 0479 } 0480 iface->call("quit"); 0481 if (iface->lastError().isValid()) { 0482 if (verbose) { 0483 fprintf(stderr, "killing %s with result\n", iface->lastError().message().toLatin1().data()); 0484 } 0485 } 0486 delete iface; 0487 } 0488 } 0489 0490 int main(int argc, char **argv, char **envp) 0491 { 0492 pid_t pid = 0; 0493 bool launch_dbus = true; 0494 bool launch_klauncher = true; 0495 bool launch_kded = true; 0496 bool suicide = false; 0497 bool listProcesses = false; 0498 bool killProcesses = false; 0499 bool listAppsInDBus = false; 0500 bool quitAppsOverDBus = false; 0501 bool shutdown = false; 0502 0503 /** Save arguments first... **/ 0504 char **safe_argv = (char **) malloc(sizeof(char *) * argc); 0505 for (int i = 0; i < argc; i++) { 0506 safe_argv[i] = strcpy((char *)malloc(strlen(argv[i]) + 1), argv[i]); 0507 if (strcmp(safe_argv[i], "--no-dbus") == 0) { 0508 launch_dbus = false; 0509 } 0510 if (strcmp(safe_argv[i], "--no-klauncher") == 0) { 0511 launch_klauncher = false; 0512 } 0513 if (strcmp(safe_argv[i], "--no-kded") == 0) { 0514 launch_kded = false; 0515 } 0516 if (strcmp(safe_argv[i], "--suicide") == 0) { 0517 suicide = true; 0518 } 0519 #ifdef ENABLE_EXIT 0520 if (strcmp(safe_argv[i], "--exit") == 0) { 0521 keep_running = 0; 0522 } 0523 #endif 0524 if (strcmp(safe_argv[i], "--verbose") == 0) { 0525 verbose = 1; 0526 } 0527 if (strcmp(safe_argv[i], "--version") == 0) { 0528 printf("Qt: %s\n", qVersion()); 0529 printf("KDE: %s\n", KINIT_VERSION_STRING); 0530 exit(0); 0531 } 0532 if (strcmp(safe_argv[i], "--help") == 0) { 0533 printf("Usage: kdeinit5 [options]\n"); 0534 #ifdef ENABLE_EXIT 0535 printf(" --exit Terminate when kded has run\n"); 0536 #endif 0537 printf(" --help this help page\n"); 0538 printf(" --list list kde processes\n"); 0539 printf(" --list-dbus-apps list all applications registered in dbus\n"); 0540 printf(" --quit-over-dbus quit all application registered in dbus\n"); 0541 printf(" --no-dbus do not start dbus-daemon\n"); 0542 printf(" --no-klauncher do not start klauncher\n"); 0543 printf(" --no-kded do not start kded\n"); 0544 printf(" --shutdown safe shutdown of all running kde processes\n"); 0545 printf(" first over dbus, then using hard kill\n"); 0546 #ifdef ENABLE_SUICIDE 0547 printf(" --suicide terminate when no KDE applications are left running\n"); 0548 #endif 0549 printf(" --terminate hard kill of *all* running kde processes\n"); 0550 printf(" --verbose print verbose messages\n"); 0551 printf(" --version Show version information\n"); 0552 exit(0); 0553 } 0554 if (strcmp(safe_argv[i], "--list") == 0) { 0555 listProcesses = true; 0556 } 0557 if (strcmp(safe_argv[i], "--shutdown") == 0) { 0558 shutdown = true; 0559 } 0560 if (strcmp(safe_argv[i], "--terminate") == 0 || strcmp(safe_argv[i], "--kill") == 0) { 0561 killProcesses = true; 0562 } 0563 if (strcmp(safe_argv[i], "--list-dbus-apps") == 0) { 0564 listAppsInDBus = true; 0565 } 0566 if (strcmp(safe_argv[i], "--quit-over-dbus") == 0) { 0567 quitAppsOverDBus = true; 0568 } 0569 } 0570 0571 PSID currentSid = getCurrentProcessOwner(); 0572 if (verbose) { 0573 fprintf(stderr, "current user sid: %s\n", qPrintable(toString(currentSid))); 0574 } 0575 ProcessList processList(currentSid); 0576 freeSid(currentSid); 0577 0578 if (listProcesses) { 0579 listAllRunningKDEProcesses(processList); 0580 return 0; 0581 } else if (killProcesses) { 0582 terminateAllRunningKDEProcesses(processList); 0583 return 0; 0584 } else if (listAppsInDBus) { 0585 listAllNamedAppsInDBus(); 0586 return 0; 0587 } else if (quitAppsOverDBus) { 0588 quitApplicationsOverDBus(); 0589 return 0; 0590 } else if (shutdown) { 0591 quitApplicationsOverDBus(); 0592 Sleep(2000); 0593 terminateAllRunningKDEProcesses(processList); 0594 } 0595 0596 #ifdef _DEBUG 0597 // first try to launch dbus-daemond in debug mode 0598 if (launch_dbus && processList.find("dbus-daemond")) { 0599 launch_dbus = false; 0600 } 0601 if (launch_dbus) { 0602 pid = launch("dbus-launchd.exe"); 0603 if (!pid) { 0604 pid = launch("dbus-launchd.bat"); 0605 } 0606 launch_dbus = (pid == 0); 0607 } 0608 #endif 0609 if (launch_dbus && !processList.find("dbus-daemon")) { 0610 if (!pid) { 0611 pid = launch("dbus-launch.exe"); 0612 } 0613 if (!pid) { 0614 pid = launch("dbus-launch.bat"); 0615 } 0616 if (!pid) { 0617 exit(1); 0618 } 0619 } 0620 0621 if (launch_klauncher && !processList.find("klauncher")) { 0622 pid = launch("klauncher"); 0623 if (!pid || !checkIfRegisteredInDBus("org.kde.klauncher5", 10)) { 0624 exit(1); 0625 } 0626 } 0627 0628 if (launch_kded && !processList.find(KDED_EXENAME)) { 0629 pid = launch(KDED_EXENAME); 0630 if (!pid || !checkIfRegisteredInDBus("org.kde.kded5", 10)) { 0631 exit(1); 0632 } 0633 } 0634 0635 for (int i = 1; i < argc; i++) { 0636 if (safe_argv[i][0] == '+') { 0637 pid = launch(safe_argv[i] + 1); 0638 } else if (safe_argv[i][0] == '-') { 0639 // Ignore 0640 } else { 0641 pid = launch(safe_argv[i]); 0642 } 0643 } 0644 0645 /** Free arguments **/ 0646 for (int i = 0; i < argc; i++) { 0647 free(safe_argv[i]); 0648 } 0649 free(safe_argv); 0650 0651 /** wait for termination of all (core) processes */ 0652 #ifdef ENABLE_SUICIDE 0653 if (suicide) { 0654 QProcess *proc; 0655 int can_exit = 1; 0656 do { 0657 for (proc : std::as_const(startedProcesses)) { 0658 if (proc->state() != QProcess::NotRunning) { 0659 can_exit = 0; 0660 } 0661 } 0662 if (!can_exit) { 0663 Sleep(2000); 0664 } 0665 } while (!can_exit); 0666 return 0; 0667 } 0668 #endif 0669 return 0; 0670 }