author | zecke <zecke> | 2003-08-25 15:00:50 (UTC) |
---|---|---|
committer | zecke <zecke> | 2003-08-25 15:00:50 (UTC) |
commit | bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44 (patch) (unidiff) | |
tree | f74d4765868000d27c45771573032515ac8db8be /core/launcher/applauncher.cpp | |
parent | 292b097e7db25dd231381c5b09307a1fbe81a492 (diff) | |
download | opie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.zip opie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.tar.gz opie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.tar.bz2 |
Initial revision
-rw-r--r-- | core/launcher/applauncher.cpp | 705 |
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 | |||
65 | const int AppLauncher::RAISE_TIMEOUT_MS = 5000; | ||
66 | |||
67 | //--------------------------------------------------------------------------- | ||
68 | |||
69 | static AppLauncher* appLauncherPtr; | ||
70 | |||
71 | const int appStopEventID = 1290; | ||
72 | |||
73 | class AppStoppedEvent : public QCustomEvent | ||
74 | { | ||
75 | public: | ||
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 | |||
82 | private: | ||
83 | int mPid, mStatus; | ||
84 | }; | ||
85 | |||
86 | AppLauncher::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 | |||
116 | AppLauncher::~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 */ | ||
131 | void 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 | |||
154 | void 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 | |||
162 | void 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 | |||
240 | void 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 | |||
255 | bool 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 | |||
266 | void 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 | ||
306 | void 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 | ||
420 | void AppLauncher::sigStopped(int sigPid, int sigStatus) | ||
421 | { | ||
422 | qDebug("Unhandled signal : AppLauncher::sigStopped(int sigPid, int sigStatus)"); | ||
423 | } | ||
424 | #endif // Q_OS_WIN32 | ||
425 | |||
426 | bool 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 | |||
445 | bool 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 | |||
484 | bool 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 | |||
607 | void 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 | |||
621 | int 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 | |||
636 | void 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 | ||
661 | void 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 | |||