KInit
kinit_win.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org> 00004 * (c) 1999 Mario Weilguni <mweilguni@sime.com> 00005 * (c) 2001 Lubos Lunak <l.lunak@kde.org> 00006 * (c) 2006-2011 Ralf Habacker <ralf.habacker@freenet.de> 00007 * (c) 2009 Patrick Spendrin <ps_ml@gmx.de> 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Library General Public 00011 * License version 2 as published by the Free Software Foundation. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Library General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Library General Public License 00019 * along with this library; see the file COPYING.LIB. If not, write to 00020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 * Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include <config.h> 00025 00026 00027 #include <errno.h> 00028 #include <stdio.h> 00029 #include <stdlib.h> 00030 #include <string.h> 00031 00032 #include <windows.h> 00033 #ifndef _WIN32_WCE 00034 #include <Sddl.h> 00035 #endif 00036 #include <tlhelp32.h> 00037 #include <psapi.h> 00038 00039 00040 #include <QtCore/QProcess> 00041 #include <QtCore/QFileInfo> 00042 // Under wince interface is defined, so undef it otherwise it breaks it 00043 #undef interface 00044 #include <QtDBus/QtDBus> 00045 00046 #include <kcomponentdata.h> 00047 #include <kstandarddirs.h> 00048 #include <kapplication.h> 00049 #include <kdeversion.h> 00050 00051 //#define ENABLE_SUICIDE 00052 //#define ENABLE_EXIT 00053 00054 #define KDED_EXENAME "kded4" 00055 00056 static KComponentData *s_instance = 0; 00057 00058 // print verbose messages 00059 int verbose=0; 00060 00062 QList<QProcess*> startedProcesses; 00063 00064 /* -------------------------------------------------------------------- 00065 sid helper - will be migrated later to a class named Sid, which could 00066 be used as base class for platform independent K_UID and K_GID types 00067 - would this be possible before KDE 5 ? 00068 --------------------------------------------------------------------- */ 00069 00076 PSID copySid(PSID from) 00077 { 00078 if (!from) 00079 return 0; 00080 int sidLength = GetLengthSid(from); 00081 PSID to = (PSID) malloc(sidLength); 00082 CopySid(sidLength, to, from); 00083 return to; 00084 } 00085 00092 void freeSid(PSID sid) 00093 { 00094 if (sid) 00095 free(sid); 00096 } 00097 00104 QString toString(PSID sid) 00105 { 00106 LPWSTR s; 00107 if (!ConvertSidToStringSid(sid, &s)) 00108 return QString(); 00109 00110 QString result = QString::fromUtf16(reinterpret_cast<ushort*>(s)); 00111 LocalFree(s); 00112 return result; 00113 } 00114 00115 /* -------------------------------------------------------------------- 00116 process helper 00117 --------------------------------------------------------------------- */ 00118 00124 static HANDLE getProcessHandle(int processID) 00125 { 00126 return OpenProcess( SYNCHRONIZE|PROCESS_QUERY_INFORMATION | 00127 PROCESS_VM_READ | PROCESS_TERMINATE, 00128 false, processID ); 00129 } 00130 00136 static QString getProcessName(DWORD pid) 00137 { 00138 HANDLE hModuleSnap = INVALID_HANDLE_VALUE; 00139 MODULEENTRY32 me32; 00140 00141 hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid ); 00142 if( hModuleSnap == INVALID_HANDLE_VALUE ) 00143 return QString(); 00144 00145 me32.dwSize = sizeof( MODULEENTRY32 ); 00146 00147 if( !Module32First( hModuleSnap, &me32 ) ) { 00148 CloseHandle( hModuleSnap ); // clean the snapshot object 00149 return QString(); 00150 } 00151 QString name = QString::fromWCharArray(me32.szExePath); 00152 CloseHandle( hModuleSnap ); 00153 return name; 00154 } 00155 00161 static PSID getProcessOwner(HANDLE hProcess) 00162 { 00163 #ifndef _WIN32_WCE 00164 HANDLE hToken = NULL; 00165 PSID sid; 00166 00167 OpenProcessToken(hProcess, TOKEN_READ, &hToken); 00168 if(hToken) 00169 { 00170 DWORD size; 00171 PTOKEN_USER userStruct; 00172 00173 // check how much space is needed 00174 GetTokenInformation(hToken, TokenUser, NULL, 0, &size); 00175 if( ERROR_INSUFFICIENT_BUFFER == GetLastError() ) 00176 { 00177 userStruct = reinterpret_cast<PTOKEN_USER>( new BYTE[size] ); 00178 GetTokenInformation(hToken, TokenUser, userStruct, size, &size); 00179 00180 sid = copySid(userStruct->User.Sid); 00181 CloseHandle(hToken); 00182 delete [] userStruct; 00183 return sid; 00184 } 00185 } 00186 #endif 00187 return 0; 00188 } 00189 00193 static PSID getCurrentProcessOwner() 00194 { 00195 return getProcessOwner(GetCurrentProcess()); 00196 } 00197 00201 class ProcessListEntry { 00202 public: 00203 ProcessListEntry( HANDLE _handle, QString _path, int _pid, PSID _owner=0 ) 00204 { 00205 QFileInfo p(_path); 00206 path = p.absolutePath(); 00207 name = p.baseName(); 00208 handle = _handle; 00209 pid = _pid; 00210 owner = copySid(_owner); 00211 } 00212 00213 ~ProcessListEntry() 00214 { 00215 freeSid(owner); 00216 CloseHandle(handle); 00217 } 00218 00219 QString name; 00220 QString path; 00221 int pid; 00222 HANDLE handle; 00223 PSID owner; 00224 friend QDebug operator <<(QDebug out, const ProcessListEntry &c); 00225 }; 00226 00227 QDebug operator <<(QDebug out, const ProcessListEntry &c) 00228 { 00229 out << "(ProcessListEntry" 00230 << "name" << c.name 00231 << "path" << c.path 00232 << "pid" << c.pid 00233 << "handle" << c.handle 00234 << "sid" << toString(c.owner) 00235 << ")"; 00236 return out; 00237 } 00238 00246 class ProcessList { 00247 public: 00252 ProcessList(PSID userSid=0); 00253 00254 ~ProcessList(); 00255 00261 ProcessListEntry *find(const QString &name); 00262 00268 bool terminateProcess(const QString &name); 00269 00274 QList<ProcessListEntry *> &list() { return m_processes; } 00275 00276 private: 00277 void init(); 00278 QList<ProcessListEntry *> m_processes; 00279 PSID m_userId; 00280 }; 00281 00282 ProcessList::ProcessList(PSID userSid) 00283 { 00284 m_userId = userSid; 00285 init(); 00286 } 00287 00288 ProcessList::~ProcessList() 00289 { 00290 foreach(const ProcessListEntry *ple,m_processes) 00291 delete ple; 00292 } 00293 00294 void ProcessList::init() 00295 { 00296 HANDLE h; 00297 PROCESSENTRY32 pe32; 00298 00299 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 00300 if (h == INVALID_HANDLE_VALUE) { 00301 return; 00302 } 00303 pe32.dwSize = sizeof(PROCESSENTRY32); 00304 if (!Process32First( h, &pe32 )) 00305 return; 00306 00307 do 00308 { 00309 HANDLE hProcess = getProcessHandle(pe32.th32ProcessID); 00310 if (!hProcess) 00311 continue; 00312 QString name = getProcessName(pe32.th32ProcessID); 00313 #ifndef _WIN32_WCE 00314 PSID sid = getProcessOwner(hProcess); 00315 if (!sid || m_userId && !EqualSid(m_userId,sid)) 00316 { 00317 freeSid(sid); 00318 continue; 00319 } 00320 #else 00321 PSID sid = 0; 00322 #endif 00323 m_processes << new ProcessListEntry( hProcess, name, pe32.th32ProcessID, sid); 00324 } while(Process32Next( h, &pe32 )); 00325 #ifndef _WIN32_WCE 00326 CloseHandle(h); 00327 #else 00328 CloseToolhelp32Snapshot(h); 00329 #endif 00330 } 00331 00332 ProcessListEntry *ProcessList::find(const QString &name) 00333 { 00334 ProcessListEntry *ple; 00335 foreach(ple,m_processes) { 00336 if (ple->pid < 0) { 00337 qDebug() << "negative pid!"; 00338 continue; 00339 } 00340 00341 if (ple->name != name && ple->name != name + ".exe") { 00342 continue; 00343 } 00344 00345 if (!ple->path.isEmpty() && !ple->path.toLower().startsWith(KStandardDirs::installPath("kdedir").toLower())) { 00346 // process is outside of installation directory 00347 qDebug() << "path of the process" << name << "seems to be outside of the installPath:" << ple->path << KStandardDirs::installPath("kdedir"); 00348 continue; 00349 } 00350 return ple; 00351 } 00352 return NULL; 00353 } 00354 00355 bool ProcessList::terminateProcess(const QString &name) 00356 { 00357 qDebug() << "going to terminate process" << name; 00358 ProcessListEntry *p = find(name); 00359 if (!p) { 00360 qDebug() << "could not find ProcessListEntry for process name" << name; 00361 return false; 00362 } 00363 00364 bool ret = TerminateProcess(p->handle,0); 00365 if (ret) { 00366 int i = m_processes.indexOf(p); 00367 if(i != -1) m_processes.removeAt(i); 00368 delete p; 00369 return true; 00370 } else { 00371 return false; 00372 } 00373 } 00374 00375 // internal launch function 00376 int launch(const QString &cmd) 00377 { 00378 QProcess *proc = new QProcess(); 00379 proc->start(cmd); 00380 proc->waitForStarted(); 00381 startedProcesses << proc; 00382 _PROCESS_INFORMATION* _pid = proc->pid(); 00383 int pid = _pid ? _pid->dwProcessId : 0; 00384 if (verbose) { 00385 fprintf(stderr,"%s",proc->readAllStandardError().constData()); 00386 fprintf(stderr,"%s",proc->readAllStandardOutput().constData()); 00387 } 00388 if (pid) { 00389 if (verbose) 00390 fprintf(stderr, "kdeinit4: Launched %s, pid = %ld\n", qPrintable(cmd),(long) pid); 00391 } 00392 else { 00393 if (verbose) 00394 fprintf(stderr, "kdeinit4: could not launch %s, exiting\n",qPrintable(cmd)); 00395 } 00396 return pid; 00397 } 00398 00400 bool checkIfRegisteredInDBus(const QString &name, int _timeout=10) 00401 { 00402 int timeout = _timeout * 5; 00403 while(timeout) { 00404 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( name ) ) 00405 break; 00406 Sleep(200); 00407 timeout--; 00408 } 00409 if (!timeout) { 00410 if (verbose) 00411 fprintf(stderr,"not registered %s in dbus after %d secs\n",qPrintable(name),_timeout); 00412 return false; 00413 } 00414 if (verbose) 00415 fprintf(stderr,"%s is registered in dbus\n",qPrintable(name)); 00416 return true; 00417 } 00418 00419 void listAllRunningKDEProcesses(ProcessList &processList) 00420 { 00421 QString installPrefix = KStandardDirs::installPath("kdedir"); 00422 00423 foreach(const ProcessListEntry *ple, processList.list()) 00424 { 00425 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower())) 00426 fprintf(stderr,"path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid); 00427 } 00428 } 00429 00430 void terminateAllRunningKDEProcesses(ProcessList &processList) 00431 { 00432 QString installPrefix = KStandardDirs::installPath("kdedir"); 00433 00434 foreach(const ProcessListEntry *ple, processList.list()) 00435 { 00436 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower())) 00437 { 00438 if (verbose) 00439 fprintf(stderr,"terminating path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid); 00440 processList.terminateProcess(ple->name); 00441 } 00442 } 00443 } 00444 00445 void listAllNamedAppsInDBus() 00446 { 00447 QDBusConnection connection = QDBusConnection::sessionBus(); 00448 QDBusConnectionInterface *bus = connection.interface(); 00449 const QStringList services = bus->registeredServiceNames(); 00450 foreach(const QString &service, services) { 00451 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':'))) 00452 continue; 00453 fprintf(stderr, "%s \n", service.toLatin1().data()); 00454 } 00455 } 00456 00457 void quitApplicationsOverDBus() 00458 { 00459 QDBusConnection connection = QDBusConnection::sessionBus(); 00460 QDBusConnectionInterface *bus = connection.interface(); 00461 const QStringList services = bus->registeredServiceNames(); 00462 foreach(const QString &service, services) { 00463 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':'))) 00464 continue; 00465 QDBusInterface *iface = new QDBusInterface(service, 00466 QLatin1String("/MainApplication"), 00467 QLatin1String("org.kde.KApplication"), 00468 connection); 00469 if (!iface->isValid()) { 00470 if (verbose) 00471 fprintf(stderr, "invalid interface of service %s\n", service.toLatin1().data()); 00472 continue; 00473 } 00474 iface->call("quit"); 00475 if (iface->lastError().isValid()) { 00476 if (verbose) 00477 fprintf(stderr,"killing %s with result\n", iface->lastError().message().toLatin1().data()); 00478 } 00479 delete iface; 00480 } 00481 } 00482 00483 int main(int argc, char **argv, char **envp) 00484 { 00485 pid_t pid = 0; 00486 bool launch_dbus = true; 00487 bool launch_klauncher = true; 00488 bool launch_kded = true; 00489 bool suicide = false; 00490 bool listProcesses = false; 00491 bool killProcesses = false; 00492 bool listAppsInDBus = false; 00493 bool quitAppsOverDBus = false; 00494 bool shutdown = false; 00495 00497 char **safe_argv = (char **) malloc( sizeof(char *) * argc); 00498 for(int i = 0; i < argc; i++) 00499 { 00500 safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]); 00501 if (strcmp(safe_argv[i], "--no-dbus") == 0) 00502 launch_dbus = false; 00503 if (strcmp(safe_argv[i], "--no-klauncher") == 0) 00504 launch_klauncher = false; 00505 if (strcmp(safe_argv[i], "--no-kded") == 0) 00506 launch_kded = false; 00507 if (strcmp(safe_argv[i], "--suicide") == 0) 00508 suicide = true; 00509 #ifdef ENABLE_EXIT 00510 if (strcmp(safe_argv[i], "--exit") == 0) 00511 keep_running = 0; 00512 #endif 00513 if (strcmp(safe_argv[i], "--verbose") == 0) 00514 verbose = 1; 00515 if (strcmp(safe_argv[i], "--version") == 0) 00516 { 00517 printf("Qt: %s\n",qVersion()); 00518 printf("KDE: %s\n", KDE_VERSION_STRING); 00519 exit(0); 00520 } 00521 if (strcmp(safe_argv[i], "--help") == 0) 00522 { 00523 printf("Usage: kdeinit4 [options]\n"); 00524 #ifdef ENABLE_EXIT 00525 printf(" --exit Terminate when kded has run\n"); 00526 #endif 00527 printf(" --help this help page\n"); 00528 printf(" --list list kde processes\n"); 00529 printf(" --list-dbus-apps list all applications registered in dbus\n"); 00530 printf(" --quit-over-dbus quit all application registered in dbus\n"); 00531 printf(" --no-dbus do not start dbus-daemon\n"); 00532 printf(" --no-klauncher do not start klauncher\n"); 00533 printf(" --no-kded do not start kded\n"); 00534 printf(" --shutdown safe shutdown of all running kde processes\n"); 00535 printf(" first over dbus, then using hard kill\n"); 00536 #ifdef ENABLE_SUICIDE 00537 printf(" --suicide terminate when no KDE applications are left running\n"); 00538 #endif 00539 printf(" --terminate hard kill of *all* running kde processes\n"); 00540 printf(" --verbose print verbose messages\n"); 00541 printf(" --version Show version information\n"); 00542 exit(0); 00543 } 00544 if (strcmp(safe_argv[i], "--list") == 0) 00545 listProcesses = true; 00546 if (strcmp(safe_argv[i], "--shutdown") == 0) 00547 shutdown = true; 00548 if (strcmp(safe_argv[i], "--terminate") == 0 || strcmp(safe_argv[i], "--kill") == 0) 00549 killProcesses = true; 00550 if (strcmp(safe_argv[i], "--list-dbus-apps") == 0) 00551 listAppsInDBus = true; 00552 if (strcmp(safe_argv[i], "--quit-over-dbus") == 0) 00553 quitAppsOverDBus = true; 00554 } 00555 00556 PSID currentSid = getCurrentProcessOwner(); 00557 if (verbose) 00558 fprintf(stderr,"current user sid: %s\n",qPrintable(toString(currentSid))); 00559 ProcessList processList(currentSid); 00560 freeSid(currentSid); 00561 00562 if (listProcesses) { 00563 listAllRunningKDEProcesses(processList); 00564 return 0; 00565 } 00566 else if (killProcesses) { 00567 terminateAllRunningKDEProcesses(processList); 00568 return 0; 00569 } 00570 else if (listAppsInDBus) { 00571 listAllNamedAppsInDBus(); 00572 return 0; 00573 } 00574 else if (quitAppsOverDBus) { 00575 quitApplicationsOverDBus(); 00576 return 0; 00577 } 00578 else if (shutdown) { 00579 quitApplicationsOverDBus(); 00580 Sleep(2000); 00581 terminateAllRunningKDEProcesses(processList); 00582 } 00583 00585 s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration); 00586 00587 #ifdef _DEBUG 00588 // first try to launch dbus-daemond in debug mode 00589 if (launch_dbus && processList.find("dbus-daemond")) 00590 launch_dbus = false; 00591 if (launch_dbus) 00592 { 00593 pid = launch("dbus-launchd.exe"); 00594 if (!pid) 00595 pid = launch("dbus-launchd.bat"); 00596 launch_dbus = (pid == 0); 00597 } 00598 #endif 00599 if (launch_dbus && !processList.find("dbus-daemon")) 00600 { 00601 if (!pid) 00602 pid = launch("dbus-launch.exe"); 00603 if (!pid) 00604 pid = launch("dbus-launch.bat"); 00605 if (!pid) 00606 exit(1); 00607 } 00608 00609 if (launch_klauncher && !processList.find("klauncher")) 00610 { 00611 pid = launch("klauncher"); 00612 if (!pid || !checkIfRegisteredInDBus("org.kde.klauncher",10)) 00613 exit(1); 00614 } 00615 00616 00617 if (launch_kded && !processList.find(KDED_EXENAME)) 00618 { 00619 pid = launch(KDED_EXENAME); 00620 if (!pid || !checkIfRegisteredInDBus("org.kde.kded",10)) 00621 exit(1); 00622 } 00623 00624 for(int i = 1; i < argc; i++) 00625 { 00626 if (safe_argv[i][0] == '+') 00627 { 00628 pid = launch(safe_argv[i]+1); 00629 } 00630 else if (safe_argv[i][0] == '-') 00631 { 00632 // Ignore 00633 } 00634 else 00635 { 00636 pid = launch( safe_argv[i]); 00637 } 00638 } 00639 00641 for(int i = 0; i < argc; i++) 00642 { 00643 free(safe_argv[i]); 00644 } 00645 free (safe_argv); 00646 00648 #ifdef ENABLE_SUICIDE 00649 if (suicide) { 00650 QProcess *proc; 00651 int can_exit=1; 00652 do { 00653 foreach(proc,startedProcesses) { 00654 if (proc->state() != QProcess::NotRunning) 00655 can_exit = 0; 00656 } 00657 if (!can_exit) 00658 Sleep(2000); 00659 } while(!can_exit); 00660 return 0; 00661 } 00662 #endif 00663 return 0; 00664 }
KDE 4.6 API Reference