Warning: That file was not part of the compilation database. It may have many parsing errors.

1/*
2 * This file is part of the KDE libraries
3 * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
4 * (c) 1999 Mario Weilguni <mweilguni@sime.com>
5 * (c) 2001 Lubos Lunak <l.lunak@kde.org>
6 * (c) 2006-2011 Ralf Habacker <ralf.habacker@freenet.de>
7 * (c) 2009 Patrick Spendrin <ps_ml@gmx.de>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License version 2 as published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include <config.h>
25
26
27#include <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include <windows.h>
33#ifndef _WIN32_WCE
34#include <Sddl.h>
35#endif
36#include <tlhelp32.h>
37#include <psapi.h>
38
39
40#include <QtCore/QProcess>
41#include <QtCore/QFileInfo>
42// Under wince interface is defined, so undef it otherwise it breaks it
43#undef interface
44#include <QtDBus/QtDBus>
45
46#include <kcomponentdata.h>
47#include <kstandarddirs.h>
48#include <kapplication.h>
49#include <kdeversion.h>
50
51//#define ENABLE_SUICIDE
52//#define ENABLE_EXIT
53
54#define KDED_EXENAME "kded4"
55
56static KComponentData *s_instance = 0;
57
58// print verbose messages
59int verbose=0;
60
61/// holds process list for suicide mode
62QList<QProcess*> startedProcesses;
63
64/* --------------------------------------------------------------------
65 sid helper - will be migrated later to a class named Sid, which could
66 be used as base class for platform independent K_UID and K_GID types
67 - would this be possible before KDE 5 ?
68 --------------------------------------------------------------------- */
69
70/**
71 copy sid
72 @param from sif to copy from
73 @return copied sid, need to be free'd with free
74 @note null sid's are handled too
75*/
76PSID copySid(PSID from)
77{
78 if (!from)
79 return 0;
80 int sidLength = GetLengthSid(from);
81 PSID to = (PSID) malloc(sidLength);
82 CopySid(sidLength, to, from);
83 return to;
84}
85
86/**
87 copy sid
88 @param from sif to copy from
89 @return copied sid, need to be free'd with free
90 @note null sid's are handled too
91*/
92void freeSid(PSID sid)
93{
94 if (sid)
95 free(sid);
96}
97
98/**
99 copy sid
100 @param from sif to copy from
101 @return copied sid, need to be free'd with free
102 @note null sid's are handled too
103*/
104QString toString(PSID sid)
105{
106 LPWSTR s;
107 if (!ConvertSidToStringSid(sid, &s))
108 return QString();
109
110 QString result = QString::fromUtf16(reinterpret_cast<ushort*>(s));
111 LocalFree(s);
112 return result;
113}
114
115/* --------------------------------------------------------------------
116 process helper
117 --------------------------------------------------------------------- */
118
119/**
120 return process handle
121 @param pid process id
122 @return process handle
123 */
124static HANDLE getProcessHandle(int processID)
125{
126 return OpenProcess( SYNCHRONIZE|PROCESS_QUERY_INFORMATION |
127 PROCESS_VM_READ | PROCESS_TERMINATE,
128 false, processID );
129}
130
131/**
132 return absolute path of process
133 @param pid process id
134 @return process name
135 */
136static QString getProcessName(DWORD pid)
137{
138 HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
139 MODULEENTRY32 me32;
140
141 hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
142 if( hModuleSnap == INVALID_HANDLE_VALUE )
143 return QString();
144
145 me32.dwSize = sizeof( MODULEENTRY32 );
146
147 if( !Module32First( hModuleSnap, &me32 ) ) {
148 CloseHandle( hModuleSnap ); // clean the snapshot object
149 return QString();
150 }
151 QString name = QString::fromWCharArray(me32.szExePath);
152 CloseHandle( hModuleSnap );
153 return name;
154}
155
156/**
157 return sid of specific process
158 @param hProcess handle to process
159 @return sid pointer to PSID structure, must be freed with LocalAlloc
160*/
161static PSID getProcessOwner(HANDLE hProcess)
162{
163#ifndef _WIN32_WCE
164 HANDLE hToken = NULL;
165 PSID sid;
166
167 OpenProcessToken(hProcess, TOKEN_READ, &hToken);
168 if(hToken)
169 {
170 DWORD size;
171 PTOKEN_USER userStruct;
172
173 // check how much space is needed
174 GetTokenInformation(hToken, TokenUser, NULL, 0, &size);
175 if( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
176 {
177 userStruct = reinterpret_cast<PTOKEN_USER>( new BYTE[size] );
178 GetTokenInformation(hToken, TokenUser, userStruct, size, &size);
179
180 sid = copySid(userStruct->User.Sid);
181 CloseHandle(hToken);
182 delete [] userStruct;
183 return sid;
184 }
185 }
186#endif
187 return 0;
188}
189
190/**
191 return sid of current process owner
192*/
193static PSID getCurrentProcessOwner()
194{
195 return getProcessOwner(GetCurrentProcess());
196}
197
198/**
199 holds single process
200 */
201class ProcessListEntry {
202 public:
203 ProcessListEntry( HANDLE _handle, QString _path, int _pid, PSID _owner=0 )
204 {
205 QFileInfo p(_path);
206 path = p.absolutePath();
207 name = p.baseName();
208 handle = _handle;
209 pid = _pid;
210 owner = copySid(_owner);
211 }
212
213 ~ProcessListEntry()
214 {
215 freeSid(owner);
216 CloseHandle(handle);
217 }
218
219 QString name;
220 QString path;
221 int pid;
222 HANDLE handle;
223 PSID owner;
224 friend QDebug operator <<(QDebug out, const ProcessListEntry &c);
225};
226
227QDebug operator <<(QDebug out, const ProcessListEntry &c)
228{
229 out << "(ProcessListEntry"
230 << "name" << c.name
231 << "path" << c.path
232 << "pid" << c.pid
233 << "handle" << c.handle
234 << "sid" << toString(c.owner)
235 << ")";
236 return out;
237}
238
239/**
240 holds system process list snapshot
241
242 Could be used as a public platform independent class or namespace in kdecore
243 for dealing with system processes, named perhaps KSystemProcessSnapshot or similar.
244 If implemented at Qt level it will be named QSystemProcessSnapshot or similar
245*/
246class ProcessList {
247public:
248 /**
249 collect process
250 @param userSid sid of user for which processes should be collected or 0 for all processes
251 */
252 ProcessList(PSID userSid=0);
253
254 ~ProcessList();
255
256 /**
257 find process in list
258 @param name process name (with or without extension)
259 @return instance of process entry
260 */
261 ProcessListEntry *find(const QString &name);
262
263 /**
264 killprocess from list
265 @param name process name (with or without extension)
266 @return ...
267 */
268 bool terminateProcess(const QString &name);
269
270 /**
271 return all processes
272 @return list with processes
273 */
274 QList<ProcessListEntry *> &list() { return m_processes; }
275
276private:
277 void init();
278 QList<ProcessListEntry *> m_processes;
279 PSID m_userId;
280};
281
282ProcessList::ProcessList(PSID userSid)
283{
284 m_userId = userSid;
285 init();
286}
287
288ProcessList::~ProcessList()
289{
290 foreach(const ProcessListEntry *ple,m_processes)
291 delete ple;
292}
293
294void ProcessList::init()
295{
296 HANDLE h;
297 PROCESSENTRY32 pe32;
298
299 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
300 if (h == INVALID_HANDLE_VALUE) {
301 return;
302 }
303 pe32.dwSize = sizeof(PROCESSENTRY32);
304 if (!Process32First( h, &pe32 ))
305 return;
306
307 do
308 {
309 HANDLE hProcess = getProcessHandle(pe32.th32ProcessID);
310 if (!hProcess)
311 continue;
312 QString name = getProcessName(pe32.th32ProcessID);
313#ifndef _WIN32_WCE
314 PSID sid = getProcessOwner(hProcess);
315 if (!sid || m_userId && !EqualSid(m_userId,sid))
316 {
317 freeSid(sid);
318 continue;
319 }
320#else
321 PSID sid = 0;
322#endif
323 m_processes << new ProcessListEntry( hProcess, name, pe32.th32ProcessID, sid);
324 } while(Process32Next( h, &pe32 ));
325#ifndef _WIN32_WCE
326 CloseHandle(h);
327#else
328 CloseToolhelp32Snapshot(h);
329#endif
330}
331
332ProcessListEntry *ProcessList::find(const QString &name)
333{
334 ProcessListEntry *ple;
335 foreach(ple,m_processes) {
336 if (ple->pid < 0) {
337 qDebug() << "negative pid!";
338 continue;
339 }
340
341 if (ple->name != name && ple->name != name + ".exe") {
342 continue;
343 }
344
345 if (!ple->path.isEmpty() && !ple->path.toLower().startsWith(KStandardDirs::installPath("kdedir").toLower())) {
346 // process is outside of installation directory
347 qDebug() << "path of the process" << name << "seems to be outside of the installPath:" << ple->path << KStandardDirs::installPath("kdedir");
348 continue;
349 }
350 return ple;
351 }
352 return NULL;
353}
354
355bool ProcessList::terminateProcess(const QString &name)
356{
357 qDebug() << "going to terminate process" << name;
358 ProcessListEntry *p = find(name);
359 if (!p) {
360 qDebug() << "could not find ProcessListEntry for process name" << name;
361 return false;
362 }
363
364 bool ret = TerminateProcess(p->handle,0);
365 if (ret) {
366 int i = m_processes.indexOf(p);
367 if(i != -1) m_processes.removeAt(i);
368 delete p;
369 return true;
370 } else {
371 return false;
372 }
373}
374
375// internal launch function
376int launch(const QString &cmd)
377{
378 QProcess *proc = new QProcess();
379 proc->start(cmd);
380 proc->waitForStarted();
381 startedProcesses << proc;
382 _PROCESS_INFORMATION* _pid = proc->pid();
383 int pid = _pid ? _pid->dwProcessId : 0;
384 if (verbose) {
385 fprintf(stderr,"%s",proc->readAllStandardError().constData());
386 fprintf(stderr,"%s",proc->readAllStandardOutput().constData());
387 }
388 if (pid) {
389 if (verbose)
390 fprintf(stderr, "kdeinit4: Launched %s, pid = %ld\n", qPrintable(cmd),(long) pid);
391 }
392 else {
393 if (verbose)
394 fprintf(stderr, "kdeinit4: could not launch %s, exiting\n",qPrintable(cmd));
395 }
396 return pid;
397}
398
399/// check dbus registration
400bool checkIfRegisteredInDBus(const QString &name, int _timeout=10)
401{
402 int timeout = _timeout * 5;
403 while(timeout) {
404 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( name ) )
405 break;
406 Sleep(200);
407 timeout--;
408 }
409 if (!timeout) {
410 if (verbose)
411 fprintf(stderr,"not registered %s in dbus after %d secs\n",qPrintable(name),_timeout);
412 return false;
413 }
414 if (verbose)
415 fprintf(stderr,"%s is registered in dbus\n",qPrintable(name));
416 return true;
417}
418
419void listAllRunningKDEProcesses(ProcessList &processList)
420{
421 QString installPrefix = KStandardDirs::installPath("kdedir");
422
423 foreach(const ProcessListEntry *ple, processList.list())
424 {
425 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower()))
426 fprintf(stderr,"path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid);
427 }
428}
429
430void terminateAllRunningKDEProcesses(ProcessList &processList)
431{
432 QString installPrefix = KStandardDirs::installPath("kdedir");
433
434 foreach(const ProcessListEntry *ple, processList.list())
435 {
436 if (!ple->path.isEmpty() && ple->path.toLower().startsWith(installPrefix.toLower()))
437 {
438 if (verbose)
439 fprintf(stderr,"terminating path: %s name: %s pid: %u\n", ple->path.toLatin1().data(), ple->name.toLatin1().data(), ple->pid);
440 processList.terminateProcess(ple->name);
441 }
442 }
443}
444
445void listAllNamedAppsInDBus()
446{
447 QDBusConnection connection = QDBusConnection::sessionBus();
448 QDBusConnectionInterface *bus = connection.interface();
449 const QStringList services = bus->registeredServiceNames();
450 foreach(const QString &service, services) {
451 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':')))
452 continue;
453 fprintf(stderr, "%s \n", service.toLatin1().data());
454 }
455}
456
457void quitApplicationsOverDBus()
458{
459 QDBusConnection connection = QDBusConnection::sessionBus();
460 QDBusConnectionInterface *bus = connection.interface();
461 const QStringList services = bus->registeredServiceNames();
462 foreach(const QString &service, services) {
463 if (service.startsWith(QLatin1String("org.freedesktop.DBus")) || service.startsWith(QLatin1Char(':')))
464 continue;
465 QDBusInterface *iface = new QDBusInterface(service,
466 QLatin1String("/MainApplication"),
467 QLatin1String("org.kde.KApplication"),
468 connection);
469 if (!iface->isValid()) {
470 if (verbose)
471 fprintf(stderr, "invalid interface of service %s\n", service.toLatin1().data());
472 continue;
473 }
474 iface->call("quit");
475 if (iface->lastError().isValid()) {
476 if (verbose)
477 fprintf(stderr,"killing %s with result\n", iface->lastError().message().toLatin1().data());
478 }
479 delete iface;
480 }
481}
482
483int main(int argc, char **argv, char **envp)
484{
485 pid_t pid = 0;
486 bool launch_dbus = true;
487 bool launch_klauncher = true;
488 bool launch_kded = true;
489 bool suicide = false;
490 bool listProcesses = false;
491 bool killProcesses = false;
492 bool listAppsInDBus = false;
493 bool quitAppsOverDBus = false;
494 bool shutdown = false;
495
496 /** Save arguments first... **/
497 char **safe_argv = (char **) malloc( sizeof(char *) * argc);
498 for(int i = 0; i < argc; i++)
499 {
500 safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]);
501 if (strcmp(safe_argv[i], "--no-dbus") == 0)
502 launch_dbus = false;
503 if (strcmp(safe_argv[i], "--no-klauncher") == 0)
504 launch_klauncher = false;
505 if (strcmp(safe_argv[i], "--no-kded") == 0)
506 launch_kded = false;
507 if (strcmp(safe_argv[i], "--suicide") == 0)
508 suicide = true;
509#ifdef ENABLE_EXIT
510 if (strcmp(safe_argv[i], "--exit") == 0)
511 keep_running = 0;
512#endif
513 if (strcmp(safe_argv[i], "--verbose") == 0)
514 verbose = 1;
515 if (strcmp(safe_argv[i], "--version") == 0)
516 {
517 printf("Qt: %s\n",qVersion());
518 printf("KDE: %s\n", KDE_VERSION_STRING);
519 exit(0);
520 }
521 if (strcmp(safe_argv[i], "--help") == 0)
522 {
523 printf("Usage: kdeinit4 [options]\n");
524#ifdef ENABLE_EXIT
525 printf(" --exit Terminate when kded has run\n");
526#endif
527 printf(" --help this help page\n");
528 printf(" --list list kde processes\n");
529 printf(" --list-dbus-apps list all applications registered in dbus\n");
530 printf(" --quit-over-dbus quit all application registered in dbus\n");
531 printf(" --no-dbus do not start dbus-daemon\n");
532 printf(" --no-klauncher do not start klauncher\n");
533 printf(" --no-kded do not start kded\n");
534 printf(" --shutdown safe shutdown of all running kde processes\n");
535 printf(" first over dbus, then using hard kill\n");
536#ifdef ENABLE_SUICIDE
537 printf(" --suicide terminate when no KDE applications are left running\n");
538#endif
539 printf(" --terminate hard kill of *all* running kde processes\n");
540 printf(" --verbose print verbose messages\n");
541 printf(" --version Show version information\n");
542 exit(0);
543 }
544 if (strcmp(safe_argv[i], "--list") == 0)
545 listProcesses = true;
546 if (strcmp(safe_argv[i], "--shutdown") == 0)
547 shutdown = true;
548 if (strcmp(safe_argv[i], "--terminate") == 0 || strcmp(safe_argv[i], "--kill") == 0)
549 killProcesses = true;
550 if (strcmp(safe_argv[i], "--list-dbus-apps") == 0)
551 listAppsInDBus = true;
552 if (strcmp(safe_argv[i], "--quit-over-dbus") == 0)
553 quitAppsOverDBus = true;
554 }
555
556 PSID currentSid = getCurrentProcessOwner();
557 if (verbose)
558 fprintf(stderr,"current user sid: %s\n",qPrintable(toString(currentSid)));
559 ProcessList processList(currentSid);
560 freeSid(currentSid);
561
562 if (listProcesses) {
563 listAllRunningKDEProcesses(processList);
564 return 0;
565 }
566 else if (killProcesses) {
567 terminateAllRunningKDEProcesses(processList);
568 return 0;
569 }
570 else if (listAppsInDBus) {
571 listAllNamedAppsInDBus();
572 return 0;
573 }
574 else if (quitAppsOverDBus) {
575 quitApplicationsOverDBus();
576 return 0;
577 }
578 else if (shutdown) {
579 quitApplicationsOverDBus();
580 Sleep(2000);
581 terminateAllRunningKDEProcesses(processList);
582 }
583
584 /** Create our instance **/
585 s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration);
586
587#ifdef _DEBUG
588 // first try to launch dbus-daemond in debug mode
589 if (launch_dbus && processList.find("dbus-daemond"))
590 launch_dbus = false;
591 if (launch_dbus)
592 {
593 pid = launch("dbus-launchd.exe");
594 if (!pid)
595 pid = launch("dbus-launchd.bat");
596 launch_dbus = (pid == 0);
597 }
598#endif
599 if (launch_dbus && !processList.find("dbus-daemon"))
600 {
601 if (!pid)
602 pid = launch("dbus-launch.exe");
603 if (!pid)
604 pid = launch("dbus-launch.bat");
605 if (!pid)
606 exit(1);
607 }
608
609 if (launch_klauncher && !processList.find("klauncher"))
610 {
611 pid = launch("klauncher");
612 if (!pid || !checkIfRegisteredInDBus("org.kde.klauncher",10))
613 exit(1);
614 }
615
616
617 if (launch_kded && !processList.find(KDED_EXENAME))
618 {
619 pid = launch(KDED_EXENAME);
620 if (!pid || !checkIfRegisteredInDBus("org.kde.kded",10))
621 exit(1);
622 }
623
624 for(int i = 1; i < argc; i++)
625 {
626 if (safe_argv[i][0] == '+')
627 {
628 pid = launch(safe_argv[i]+1);
629 }
630 else if (safe_argv[i][0] == '-')
631 {
632 // Ignore
633 }
634 else
635 {
636 pid = launch( safe_argv[i]);
637 }
638 }
639
640 /** Free arguments **/
641 for(int i = 0; i < argc; i++)
642 {
643 free(safe_argv[i]);
644 }
645 free (safe_argv);
646
647 /** wait for termination of all (core) processes */
648#ifdef ENABLE_SUICIDE
649 if (suicide) {
650 QProcess *proc;
651 int can_exit=1;
652 do {
653 foreach(proc,startedProcesses) {
654 if (proc->state() != QProcess::NotRunning)
655 can_exit = 0;
656 }
657 if (!can_exit)
658 Sleep(2000);
659 } while(!can_exit);
660 return 0;
661 }
662#endif
663 return 0;
664}
665

Warning: That file was not part of the compilation database. It may have many parsing errors.