summaryrefslogtreecommitdiff
path: root/core/launcher/applauncher.cpp
Unidiff
Diffstat (limited to 'core/launcher/applauncher.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/launcher/applauncher.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/core/launcher/applauncher.cpp b/core/launcher/applauncher.cpp
new file mode 100644
index 0000000..50c1b71
--- a/dev/null
+++ b/core/launcher/applauncher.cpp
@@ -0,0 +1,705 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of the Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#ifndef QTOPIA_INTERNAL_PRELOADACCESS
22#define QTOPIA_INTERNAL_PRELOADACCESS
23#endif
24#ifndef QTOPIA_INTERNAL_FILEOPERATIONS
25#define QTOPIA_INTERNAL_FILEOPERATIONS
26#endif
27#ifndef QTOPIA_PROGRAM_MONITOR
28#define QTOPIA_PROGRAM_MONITOR
29#endif
30#include <qtopia/qpeglobal.h>
31
32#ifndef Q_OS_WIN32
33#include <sys/stat.h>
34#include <sys/wait.h>
35#include <sys/file.h>
36#include <unistd.h>
37#include <sys/time.h>
38#include <sys/resource.h>
39#include <errno.h>
40#else
41#include <process.h>
42#include <windows.h>
43#include <winbase.h>
44#endif
45
46#include <signal.h>
47#include <sys/types.h>
48#include <stdlib.h>
49
50#include <qtimer.h>
51#include <qwindowsystem_qws.h>
52#include <qmessagebox.h>
53#include <qfile.h>
54#include <qfileinfo.h>
55
56#include <qtopia/qcopenvelope_qws.h>
57#include <qtopia/applnk.h>
58#include <qtopia/qpeapplication.h>
59#include <qtopia/config.h>
60#include <qtopia/global.h>
61
62#include "applauncher.h"
63#include "documentlist.h"
64
65const int AppLauncher::RAISE_TIMEOUT_MS = 5000;
66
67//---------------------------------------------------------------------------
68
69static AppLauncher* appLauncherPtr;
70
71const int appStopEventID = 1290;
72
73class AppStoppedEvent : public QCustomEvent
74{
75public:
76 AppStoppedEvent(int pid, int status)
77 : QCustomEvent( appStopEventID ), mPid(pid), mStatus(status) { }
78
79 int pid() { return mPid; }
80 int status() { return mStatus; }
81
82private:
83 int mPid, mStatus;
84};
85
86AppLauncher::AppLauncher(QObject *parent, const char *name)
87 : QObject(parent, name), qlPid(0), qlReady(FALSE),
88 appKillerBox(0)
89{
90 connect(qwsServer, SIGNAL(newChannel(const QString&)), this, SLOT(newQcopChannel(const QString&)));
91 connect(qwsServer, SIGNAL(removedChannel(const QString&)), this, SLOT(removedQcopChannel(const QString&)));
92 QCopChannel* channel = new QCopChannel( "QPE/System", this );
93 connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
94 this, SLOT(received(const QCString&, const QByteArray&)) );
95
96 channel = new QCopChannel( "QPE/Server", this );
97 connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
98 this, SLOT(received(const QCString&, const QByteArray&)) );
99
100#ifndef Q_OS_WIN32
101 signal(SIGCHLD, signalHandler);
102#else
103 runningAppsProc.setAutoDelete( TRUE );
104#endif
105 QString tmp = qApp->argv()[0];
106 int pos = tmp.findRev('/');
107 if ( pos > -1 )
108 tmp = tmp.mid(++pos);
109 runningApps[::getpid()] = tmp;
110
111 appLauncherPtr = this;
112
113 QTimer::singleShot( 1000, this, SLOT(createQuickLauncher()) );
114}
115
116AppLauncher::~AppLauncher()
117{
118 appLauncherPtr = 0;
119#ifndef Q_OS_WIN32
120 signal(SIGCHLD, SIG_DFL);
121#endif
122 if ( qlPid ) {
123 int status;
124 ::kill( qlPid, SIGTERM );
125 waitpid( qlPid, &status, 0 );
126 }
127}
128
129/* We use the QCopChannel of the app as an indicator of when it has been launched
130 so that we can disable the busy indicators */
131void AppLauncher::newQcopChannel(const QString& channelName)
132{
133// qDebug("channel %s added", channelName.data() );
134 QString prefix("QPE/Application/");
135 if (channelName.startsWith(prefix)) {
136 {
137 QCopEnvelope e("QPE/System", "newChannel(QString)");
138 e << channelName;
139 }
140 QString appName = channelName.mid(prefix.length());
141 if ( appName != "quicklauncher" ) {
142 emit connected( appName );
143 QCopEnvelope e("QPE/System", "notBusy(QString)");
144 e << appName;
145 }
146 } else if (channelName.startsWith("QPE/QuickLauncher-")) {
147 qDebug("Registered %s", channelName.latin1());
148 int pid = channelName.mid(18).toInt();
149 if (pid == qlPid)
150 qlReady = TRUE;
151 }
152}
153
154void AppLauncher::removedQcopChannel(const QString& channelName)
155{
156 if (channelName.startsWith("QPE/Application/")) {
157 QCopEnvelope e("QPE/System", "removedChannel(QString)");
158 e << channelName;
159 }
160}
161
162void AppLauncher::received(const QCString& msg, const QByteArray& data)
163{
164 QDataStream stream( data, IO_ReadOnly );
165 if ( msg == "execute(QString)" ) {
166 QString t;
167 stream >> t;
168 if ( !executeBuiltin( t, QString::null ) )
169 execute(t, QString::null);
170 } else if ( msg == "execute(QString,QString)" ) {
171 QString t,d;
172 stream >> t >> d;
173 if ( !executeBuiltin( t, d ) )
174 execute( t, d );
175 } else if ( msg == "processQCop(QString)" ) { // from QPE/Server
176 QString t;
177 stream >> t;
178 if ( !executeBuiltin( t, QString::null ) )
179 execute( t, QString::null, TRUE);
180 } else if ( msg == "raise(QString)" ) {
181 QString appName;
182 stream >> appName;
183
184 if ( !executeBuiltin( appName, QString::null ) ) {
185 if ( !waitingHeartbeat.contains( appName ) && appKillerName != appName ) {
186 //qDebug( "Raising: %s", appName.latin1() );
187 QCString channel = "QPE/Application/";
188 channel += appName.latin1();
189
190 // Need to lock it to avoid race conditions with QPEApplication::processQCopFile
191 QFile f("/tmp/qcop-msg-" + appName);
192 if ( f.open(IO_WriteOnly | IO_Append) ) {
193#ifndef Q_OS_WIN32
194 flock(f.handle(), LOCK_EX);
195#endif
196 QDataStream ds(&f);
197 QByteArray b;
198 QDataStream bstream(b, IO_WriteOnly);
199 ds << channel << QCString("raise()") << b;
200 f.flush();
201#ifndef Q_OS_WIN32
202 flock(f.handle(), LOCK_UN);
203#endif
204 f.close();
205 }
206 bool alreadyRunning = isRunning( appName );
207 if ( execute(appName, QString::null) ) {
208 int id = startTimer(RAISE_TIMEOUT_MS + alreadyRunning?2000:0);
209 waitingHeartbeat.insert( appName, id );
210 }
211 }
212 }
213 } else if ( msg == "sendRunningApps()" ) {
214 QStringList apps;
215 QMap<int,QString>::Iterator it;
216 for( it = runningApps.begin(); it != runningApps.end(); ++it )
217 apps.append( *it );
218 QCopEnvelope e( "QPE/Desktop", "runningApps(QStringList)" );
219 e << apps;
220 } else if ( msg == "appRaised(QString)" ) {
221 QString appName;
222 stream >> appName;
223 qDebug("Got a heartbeat from %s", appName.latin1());
224 QMap<QString,int>::Iterator it = waitingHeartbeat.find(appName);
225 if ( it != waitingHeartbeat.end() ) {
226 killTimer( *it );
227 waitingHeartbeat.remove(it);
228 }
229 // Check to make sure we're not waiting on user input...
230 if ( appKillerBox && appName == appKillerName ) {
231 // If we are, we kill the dialog box, and the code waiting on the result
232 // will clean us up (basically the user said "no").
233 delete appKillerBox;
234 appKillerBox = 0;
235 appKillerName = QString::null;
236 }
237 }
238}
239
240void AppLauncher::signalHandler(int)
241{
242#ifndef Q_OS_WIN32
243 int status;
244 pid_t pid = waitpid(-1, &status, WNOHANG);
245/* if (pid == 0 || &status == 0 ) {
246 qDebug("hmm, could not get return value from signal");
247 }
248*/
249 QApplication::postEvent(appLauncherPtr, new AppStoppedEvent(pid, status) );
250#else
251 qDebug("Unhandled signal see by AppLauncher::signalHandler(int)");
252#endif
253}
254
255bool AppLauncher::event(QEvent *e)
256{
257 if ( e->type() == appStopEventID ) {
258 AppStoppedEvent *ae = (AppStoppedEvent *) e;
259 sigStopped(ae->pid(), ae->status() );
260 return TRUE;
261 }
262
263 return QObject::event(e);
264}
265
266void AppLauncher::timerEvent( QTimerEvent *e )
267{
268 int id = e->timerId();
269 QMap<QString,int>::Iterator it;
270 for ( it = waitingHeartbeat.begin(); it != waitingHeartbeat.end(); ++it ) {
271 if ( *it == id ) {
272 if ( appKillerBox ) // we're already dealing with one
273 return;
274
275 appKillerName = it.key();
276 killTimer( id );
277 waitingHeartbeat.remove( it );
278
279 // qDebug("Checking in on %s", appKillerName.latin1());
280
281 // We store this incase the application responds while we're
282 // waiting for user input so we know not to delete ourselves.
283 appKillerBox = new QMessageBox(tr("Application Problem"),
284 tr("<p>%1 is not responding.</p>").arg(appKillerName) +
285 tr("<p>Would you like to force the application to exit?</p>"),
286 QMessageBox::Warning, QMessageBox::Yes,
287 QMessageBox::No | QMessageBox::Default,
288 QMessageBox::NoButton);
289 if (appKillerBox->exec() == QMessageBox::Yes) {
290 // qDebug("Killing the app!!! Bwuhahahaha!");
291 int pid = pidForName(appKillerName);
292 if ( pid > 0 )
293 kill( pid );
294 }
295 appKillerName = QString::null;
296 delete appKillerBox;
297 appKillerBox = 0;
298 return;
299 }
300 }
301
302 QObject::timerEvent( e );
303}
304
305#ifndef Q_OS_WIN32
306void AppLauncher::sigStopped(int sigPid, int sigStatus)
307{
308 int exitStatus = 0;
309
310 bool crashed = WIFSIGNALED(sigStatus);
311 if ( !crashed ) {
312 if ( WIFEXITED(sigStatus) )
313 exitStatus = WEXITSTATUS(sigStatus);
314 } else {
315 exitStatus = WTERMSIG(sigStatus);
316 }
317
318 QMap<int,QString>::Iterator it = runningApps.find( sigPid );
319 if ( it == runningApps.end() ) {
320 if ( sigPid == qlPid ) {
321 qDebug( "quicklauncher stopped" );
322 qlPid = 0;
323 qlReady = FALSE;
324 QFile::remove("/tmp/qcop-msg-quicklauncher" );
325 QTimer::singleShot( 2000, this, SLOT(createQuickLauncher()) );
326 }
327/*
328 if ( sigPid == -1 )
329 qDebug("non-qtopia application exited (disregarded)");
330 else
331 qDebug("==== no pid matching %d in list, definite bug", sigPid);
332*/
333 return;
334 }
335 QString appName = *it;
336 runningApps.remove(it);
337
338 QMap<QString,int>::Iterator hbit = waitingHeartbeat.find(appName);
339 if ( hbit != waitingHeartbeat.end() ) {
340 killTimer( *hbit );
341 waitingHeartbeat.remove( hbit );
342 }
343 if ( appName == appKillerName ) {
344 appKillerName = QString::null;
345 delete appKillerBox;
346 appKillerBox = 0;
347 }
348
349 /* we must disable preload for an app that crashes as the system logic relies on preloaded apps
350 actually being loaded. If eg. the crash happened in the constructor, we can't automatically reload
351 the app (withouth some timeout value for eg. 3 tries (which I think is a bad solution)
352 */
353 bool preloadDisabled = FALSE;
354 if ( !DocumentList::appLnkSet ) return;
355 const AppLnk* app = DocumentList::appLnkSet->findExec( appName );
356 if ( !app ) return; // QCop messages processed to slow?
357 if ( crashed && app->isPreloaded() ) {
358 Config cfg("Launcher");
359 cfg.setGroup("Preload");
360 QStringList apps = cfg.readListEntry("Apps",',');
361 QString exe = app->exec();
362 apps.remove(exe);
363 cfg.writeEntry("Apps",apps,',');
364 preloadDisabled = TRUE;
365 }
366
367 // clean up
368 if ( exitStatus ) {
369 QCopEnvelope e("QPE/System", "notBusy(QString)");
370 e << app->exec();
371 }
372/*
373 // debug info
374 for (it = runningApps.begin(); it != runningApps.end(); ++it) {
375 qDebug("running according to internal list: %s, with pid %d", (*it).data(), it.key() );
376 }
377*/
378
379#ifdef QTOPIA_PROGRAM_MONITOR
380 if ( crashed ) {
381 QString sig;
382 switch( exitStatus ) {
383 case SIGABRT: sig = "SIGABRT"; break;
384 case SIGALRM: sig = "SIGALRM"; break;
385 case SIGBUS: sig = "SIGBUS"; break;
386 case SIGFPE: sig = "SIGFPE"; break;
387 case SIGHUP: sig = "SIGHUP"; break;
388 case SIGILL: sig = "SIGILL"; break;
389 case SIGKILL: sig = "SIGKILL"; break;
390 case SIGPIPE: sig = "SIGPIPE"; break;
391 case SIGQUIT: sig = "SIGQUIT"; break;
392 case SIGSEGV: sig = "SIGSEGV"; break;
393 case SIGTERM: sig = "SIGTERM"; break;
394 case SIGTRAP: sig = "SIGTRAP"; break;
395 default: sig = QString("Unkown %1").arg(exitStatus);
396 }
397 if ( preloadDisabled )
398 sig += tr("<qt><p>Fast loading has been disabled for this application. Tap and hold the application icon to reenable it.</qt>");
399
400 QString str = tr("<qt><b>%1</b> was terminated due to signal code %2</qt>").arg( app->name() ).arg( sig );
401 QMessageBox::information(0, tr("Application terminated"), str );
402 } else {
403 if ( exitStatus == 255 ) { //could not find app (because global returns -1)
404 QMessageBox::information(0, tr("Application not found"), tr("<qt>Could not locate application <b>%1</b></qt>").arg( app->exec() ) );
405 } else {
406 QFileInfo fi(Global::tempDir() + "qcop-msg-" + appName);
407 if ( fi.exists() && fi.size() ) {
408 emit terminated(sigPid, appName);
409 execute( appName, QString::null );
410 return;
411 }
412 }
413 }
414
415#endif
416
417 emit terminated(sigPid, appName);
418}
419#else
420void AppLauncher::sigStopped(int sigPid, int sigStatus)
421{
422 qDebug("Unhandled signal : AppLauncher::sigStopped(int sigPid, int sigStatus)");
423}
424#endif // Q_OS_WIN32
425
426bool AppLauncher::isRunning(const QString &app)
427{
428 for (QMap<int,QString>::ConstIterator it = runningApps.begin(); it != runningApps.end(); ++it) {
429 if ( *it == app ) {
430#ifdef Q_OS_UNIX
431 pid_t t = ::__getpgid( it.key() );
432 if ( t == -1 ) {
433 qDebug("appLauncher bug, %s believed running, but pid %d is not existing", app.data(), it.key() );
434 runningApps.remove( it.key() );
435 return FALSE;
436 }
437#endif
438 return TRUE;
439 }
440 }
441
442 return FALSE;
443}
444
445bool AppLauncher::executeBuiltin(const QString &c, const QString &document)
446{
447 Global::Command* builtin = Global::builtinCommands();
448 QGuardedPtr<QWidget> *running = Global::builtinRunning();
449
450 // Attempt to execute the app using a builtin class for the app
451 if (builtin) {
452 for (int i = 0; builtin[i].file; i++) {
453 if ( builtin[i].file == c ) {
454 if ( running[i] ) {
455 if ( !document.isNull() && builtin[i].documentary )
456 Global::setDocument(running[i], document);
457 running[i]->raise();
458 running[i]->show();
459 running[i]->setActiveWindow();
460 } else {
461 running[i] = builtin[i].func( builtin[i].maximized );
462 }
463#ifndef QT_NO_COP
464 QCopEnvelope e("QPE/System", "notBusy(QString)" );
465 e << c; // that was quick ;-)
466#endif
467 return TRUE;
468 }
469 }
470 }
471
472 // Convert the command line in to a list of arguments
473 QStringList list = QStringList::split(QRegExp(" *"),c);
474 QString ap=list[0];
475
476 if ( ap == "suspend" ) { // No tr
477 QWSServer::processKeyEvent( 0xffff, Qt::Key_F34, FALSE, TRUE, FALSE );
478 return TRUE;
479 }
480
481 return FALSE;
482}
483
484bool AppLauncher::execute(const QString &c, const QString &docParam, bool noRaise)
485{
486 // Convert the command line in to a list of arguments
487 QStringList list = QStringList::split(QRegExp(" *"),c);
488 if ( !docParam.isEmpty() )
489 list.append( docParam );
490
491 QString appName = list[0];
492 if ( isRunning(appName) ) {
493 QCString channel = "QPE/Application/";
494 channel += appName.latin1();
495
496 // Need to lock it to avoid race conditions with QPEApplication::processQCopFile
497 QFile f(Global::tempDir() + "qcop-msg-" + appName);
498 if ( !noRaise && f.open(IO_WriteOnly | IO_Append) ) {
499#ifndef Q_OS_WIN32
500 flock(f.handle(), LOCK_EX);
501#endif
502
503 QDataStream ds(&f);
504 QByteArray b;
505 QDataStream bstream(b, IO_WriteOnly);
506 if ( !f.size() ) {
507 ds << channel << QCString("raise()") << b;
508 if ( !waitingHeartbeat.contains( appName ) && appKillerName != appName ) {
509 int id = startTimer(RAISE_TIMEOUT_MS);
510 waitingHeartbeat.insert( appName, id );
511 }
512 }
513 if ( !docParam.isEmpty() ) {
514 bstream << docParam;
515 ds << channel << QCString("setDocument(QString)") << b;
516 }
517
518 f.flush();
519#ifndef Q_OS_WIN32
520 flock(f.handle(), LOCK_UN);
521#endif
522 f.close();
523 }
524 if ( QCopChannel::isRegistered(channel) ) // avoid unnecessary warnings
525 QCopChannel::send(channel,"QPEProcessQCop()");
526
527 return TRUE;
528 }
529
530#ifdef QT_NO_QWS_MULTIPROCESS
531 QMessageBox::warning( 0, tr("Error"), tr("Could not find the application %1").arg(c),
532 tr("OK"), 0, 0, 0, 1 );
533#else
534
535 QStrList slist;
536 unsigned j;
537 for ( j = 0; j < list.count(); j++ )
538 slist.append( list[j].utf8() );
539
540 const char **args = new const char *[slist.count() + 1];
541 for ( j = 0; j < slist.count(); j++ )
542 args[j] = slist.at(j);
543 args[j] = NULL;
544
545#ifndef Q_OS_WIN32
546 if ( qlPid && qlReady && QFile::exists( QPEApplication::qpeDir()+"plugins/application/lib"+args[0] + ".so" ) ) {
547 qDebug( "Quick launching: %s", args[0] );
548 if ( getuid() == 0 )
549 setpriority( PRIO_PROCESS, qlPid, 0 );
550 QCString qlch("QPE/QuickLauncher-");
551 qlch += QString::number(qlPid);
552 QCopEnvelope env( qlch, "execute(QStrList)" );
553 env << slist;
554 runningApps[qlPid] = QString(args[0]);
555 emit launched(qlPid, QString(args[0]));
556 QCopEnvelope e("QPE/System", "busy()");
557 qlPid = 0;
558 qlReady = FALSE;
559 QTimer::singleShot( getuid() == 0 ? 800 : 1500, this, SLOT(createQuickLauncher()) );
560 } else {
561 int pid = ::vfork();
562 if ( !pid ) {
563 for ( int fd = 3; fd < 100; fd++ )
564 ::close( fd );
565 ::setpgid( ::getpid(), ::getppid() );
566 // Try bindir first, so that foo/bar works too
567 ::execv( QPEApplication::qpeDir()+"bin/"+args[0], (char * const *)args );
568 ::execvp( args[0], (char * const *)args );
569 _exit( -1 );
570 }
571
572 runningApps[pid] = QString(args[0]);
573 emit launched(pid, QString(args[0]));
574 QCopEnvelope e("QPE/System", "busy()");
575 }
576#else
577 QProcess *proc = new QProcess(this);
578 if (proc){
579 for (int i=0; i < slist.count(); i++)
580 proc->addArgument(args[i]);
581 connect(proc, SIGNAL(processExited()), this, SLOT(processExited()));
582 if (!proc->start()){
583 qDebug("Unable to start application %s", args[0]);
584 }else{
585 PROCESS_INFORMATION *procInfo = (PROCESS_INFORMATION *)proc->processIdentifier();
586 if (procInfo){
587 DWORD pid = procInfo->dwProcessId;
588 runningApps[pid] = QString(args[0]);
589 runningAppsProc.append(proc);
590 emit launched(pid, QString(args[0]));
591 QCopEnvelope e("QPE/System", "busy()");
592 }else{
593 qDebug("Unable to read process inforation #1 for %s", args[0]);
594 }
595 }
596 }else{
597 qDebug("Unable to create process for application %s", args[0]);
598 return FALSE;
599 }
600#endif
601#endif //QT_NO_QWS_MULTIPROCESS
602
603 delete [] args;
604 return TRUE;
605}
606
607void AppLauncher::kill( int pid )
608{
609#ifndef Q_OS_WIN32
610 ::kill( pid, SIGTERM );
611#else
612 for ( QProcess *proc = runningAppsProc.first(); proc; proc = runningAppsProc.next() ) {
613 if ( proc->processIdentifier() == pid ) {
614 proc->kill();
615 break;
616 }
617 }
618#endif
619}
620
621int AppLauncher::pidForName( const QString &appName )
622{
623 int pid = -1;
624
625 QMap<int, QString>::Iterator it;
626 for (it = runningApps.begin(); it!= runningApps.end(); ++it) {
627 if (*it == appName) {
628 pid = it.key();
629 break;
630 }
631 }
632
633 return pid;
634}
635
636void AppLauncher::createQuickLauncher()
637{
638 qlReady = FALSE;
639 qlPid = ::vfork();
640 if ( !qlPid ) {
641 char **args = new char *[2];
642 args[0] = "quicklauncher";
643 args[1] = 0;
644 for ( int fd = 3; fd < 100; fd++ )
645 ::close( fd );
646 ::setpgid( ::getpid(), ::getppid() );
647 // Try bindir first, so that foo/bar works too
648 setenv( "LD_BIND_NOW", "1", 1 );
649 ::execv( QPEApplication::qpeDir()+"bin/quicklauncher", args );
650 ::execvp( "quicklauncher", args );
651 _exit( -1 );
652 } else if ( qlPid == -1 ) {
653 qlPid = 0;
654 } else {
655 if ( getuid() == 0 )
656 setpriority( PRIO_PROCESS, qlPid, 19 );
657 }
658}
659
660// Used only by Win32
661void AppLauncher::processExited()
662{
663#ifdef Q_OS_WIN32
664 qDebug("AppLauncher::processExited()");
665 bool found = FALSE;
666 QProcess *proc = (QProcess *) sender();
667 if (!proc){
668 qDebug("Interanl error NULL proc");
669 return;
670 }
671
672 QString appName = proc->arguments()[0];
673 qDebug("Removing application %s", appName.latin1());
674 runningAppsProc.remove(proc);
675
676 QMap<QString,int>::Iterator hbit = waitingHeartbeat.find(appName);
677 if ( hbit != waitingHeartbeat.end() ) {
678 killTimer( *hbit );
679 waitingHeartbeat.remove( hbit );
680 }
681 if ( appName == appKillerName ) {
682 appKillerName = QString::null;
683 delete appKillerBox;
684 appKillerBox = 0;
685 }
686
687 // Search for the app to find its PID
688 QMap<int, QString>::Iterator it;
689 for (it = runningApps.begin(); it!= runningApps.end(); ++it){
690 if (it.data() == appName){
691 found = TRUE;
692 break;
693 }
694 }
695
696 if (found){
697 emit terminated(it.key(), it.data());
698 runningApps.remove(it.key());
699 }else{
700 qDebug("Internal error application %s not listed as running", appName.latin1());
701 }
702
703#endif
704}
705