fix logic
[personal-kdelibs.git] / kinit / klauncher.cpp
blob977657613a0b459b281ef58f1de9b8f478f1f60e
1 /*
2 This file is part of the KDE libraries
3 Copyright (c) 1999 Waldo Bastian <bastian@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "klauncher.h"
21 #include "klauncher_cmds.h"
22 #include "klauncher_adaptor.h"
24 #include <config.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <sys/time.h>
33 #ifdef Q_WS_X11
34 #include <kstartupinfo.h>
35 #include <X11/Xlib.h>
36 #endif
38 #include <QtCore/QFile>
40 #include <kconfig.h>
41 #include <kdebug.h>
42 #include <kde_file.h>
43 #include <klibloader.h>
44 #include <klocale.h>
45 #include <kprotocolmanager.h>
46 #include <kprotocolinfo.h>
47 #include <krun.h>
48 #include <kstandarddirs.h>
49 #include <ktemporaryfile.h>
50 #include <kurl.h>
52 #include <kio/global.h>
53 #include <kio/connection.h>
54 #include <kio/slaveinterface.h>
56 // Dispose slaves after being idle for SLAVE_MAX_IDLE seconds
57 #define SLAVE_MAX_IDLE 30
59 // #define KLAUNCHER_VERBOSE_OUTPUT
61 using namespace KIO;
63 IdleSlave::IdleSlave(QObject *parent)
64 : QObject(parent)
66 QObject::connect(&mConn, SIGNAL(readyRead()), this, SLOT(gotInput()));
67 // Send it a SLAVE_STATUS command.
68 mConn.send( CMD_SLAVE_STATUS );
69 mPid = 0;
70 mBirthDate = time(0);
71 mOnHold = false;
74 template<int T> struct PIDType { typedef pid_t PID_t; } ;
75 template<> struct PIDType<2> { typedef qint16 PID_t; } ;
76 template<> struct PIDType<4> { typedef qint32 PID_t; } ;
78 void
79 IdleSlave::gotInput()
81 int cmd;
82 QByteArray data;
83 if (mConn.read( &cmd, data) == -1)
85 // Communication problem with slave.
86 kError(7016) << "SlavePool: No communication with slave." << endl;
87 deleteLater();
89 else if (cmd == MSG_SLAVE_ACK)
91 deleteLater();
93 else if (cmd != MSG_SLAVE_STATUS)
95 kError(7016) << "SlavePool: Unexpected data from slave." << endl;
96 deleteLater();
98 else
100 QDataStream stream( data );
101 PIDType<sizeof(pid_t)>::PID_t stream_pid;
102 pid_t pid;
103 QByteArray protocol;
104 QString host;
105 qint8 b;
106 stream >> stream_pid >> protocol >> host >> b;
107 pid = stream_pid;
108 // Overload with (bool) onHold, (KUrl) url.
109 if (!stream.atEnd())
111 KUrl url;
112 stream >> url;
113 mOnHold = true;
114 mUrl = url;
117 mPid = pid;
118 mConnected = (b != 0);
119 mProtocol = QString::fromLatin1(protocol);
120 mHost = host;
121 emit statusUpdate(this);
125 void
126 IdleSlave::connect(const QString &app_socket)
128 QByteArray data;
129 QDataStream stream( &data, QIODevice::WriteOnly);
130 stream << app_socket;
131 mConn.send( CMD_SLAVE_CONNECT, data );
132 // Timeout!
135 void
136 IdleSlave::reparseConfiguration()
138 mConn.send( CMD_REPARSECONFIGURATION );
141 bool
142 IdleSlave::match(const QString &protocol, const QString &host, bool needConnected)
144 if (mOnHold || protocol != mProtocol) {
145 return false;
147 if (host.isEmpty()) {
148 return true;
150 return (host == mHost) && (!needConnected || mConnected);
153 bool
154 IdleSlave::onHold(const KUrl &url)
156 if (!mOnHold) return false;
157 return (url == mUrl);
161 IdleSlave::age(time_t now)
163 return (int) difftime(now, mBirthDate);
166 static KLauncher* g_klauncher_self;
168 KLauncher::KLauncher(int _kdeinitSocket)
169 : QObject(0),
170 kdeinitSocket(_kdeinitSocket), dontBlockReading(false)
172 #ifdef Q_WS_X11
173 mCached_dpy = NULL;
174 #endif
175 Q_ASSERT( g_klauncher_self == NULL );
176 g_klauncher_self = this;
178 mAutoTimer.setSingleShot(true);
179 new KLauncherAdaptor(this);
180 QDBusConnection::sessionBus().registerObject("/KLauncher", this); // same as ktoolinvocation.cpp
182 connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart()));
183 connect(QDBusConnection::sessionBus().interface(),
184 SIGNAL(serviceOwnerChanged(QString,QString,QString)),
185 SLOT(slotNameOwnerChanged(QString,QString,QString)));
187 mConnectionServer.listenForRemote();
188 connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave()));
189 if (!mConnectionServer.isListening())
191 // Severe error!
192 qDebug("KLauncher: Fatal error, can't create tempfile!");
193 ::_exit(1);
196 connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout()));
198 #ifndef Q_WS_WIN
199 kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read);
200 connect(kdeinitNotifier, SIGNAL( activated( int )),
201 this, SLOT( slotKDEInitData( int )));
202 kdeinitNotifier->setEnabled( true );
203 #endif
204 lastRequest = 0;
205 bProcessingQueue = false;
207 mSlaveDebug = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_DEBUG_WAIT"));
208 if (!mSlaveDebug.isEmpty())
210 qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", qPrintable(mSlaveDebug));
212 mSlaveValgrind = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND"));
213 if (!mSlaveValgrind.isEmpty())
215 mSlaveValgrindSkin = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND_SKIN"));
216 qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", qPrintable(mSlaveValgrind));
218 #ifdef Q_WS_WIN
219 kDebug(7016) << "LAUNCHER_OK";
220 #else
221 klauncher_header request_header;
222 request_header.cmd = LAUNCHER_OK;
223 request_header.arg_length = 0;
224 write(kdeinitSocket, &request_header, sizeof(request_header));
225 #endif
228 KLauncher::~KLauncher()
230 close();
231 g_klauncher_self = NULL;
234 void KLauncher::close()
236 #ifdef Q_WS_X11
237 if( mCached_dpy != NULL )
239 XCloseDisplay( mCached_dpy );
240 mCached_dpy = NULL;
242 #endif
245 void
246 KLauncher::destruct()
248 if (g_klauncher_self)
249 g_klauncher_self->close();
250 // We don't delete the app here, that's intentional.
251 ::_exit(255);
254 void KLauncher::setLaunchEnv(const QString &name, const QString &value)
256 #ifdef Q_WS_WIN
258 #else
259 klauncher_header request_header;
260 QByteArray requestData;
261 requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0');
262 request_header.cmd = LAUNCHER_SETENV;
263 request_header.arg_length = requestData.size();
264 write(kdeinitSocket, &request_header, sizeof(request_header));
265 write(kdeinitSocket, requestData.data(), request_header.arg_length);
266 #endif
269 #ifndef Q_WS_WIN
271 * Read 'len' bytes from 'sock' into buffer.
272 * returns -1 on failure, 0 on no data.
274 static int
275 read_socket(int sock, char *buffer, int len)
277 ssize_t result;
278 int bytes_left = len;
279 while ( bytes_left > 0)
281 result = read(sock, buffer, bytes_left);
282 if (result > 0)
284 buffer += result;
285 bytes_left -= result;
287 else if (result == 0)
288 return -1;
289 else if ((result == -1) && (errno != EINTR))
290 return -1;
292 return 0;
296 void
297 KLauncher::slotKDEInitData(int)
299 klauncher_header request_header;
300 QByteArray requestData;
301 if( dontBlockReading )
303 // in case we get a request to start an application and data arrive
304 // to kdeinitSocket at the same time, requestStart() will already
305 // call slotKDEInitData(), so we must check there's still something
306 // to read, otherwise this would block
307 fd_set in;
308 timeval tm = { 0, 0 };
309 FD_ZERO ( &in );
310 FD_SET( kdeinitSocket, &in );
311 select( kdeinitSocket + 1, &in, 0, 0, &tm );
312 if( !FD_ISSET( kdeinitSocket, &in ))
313 return;
315 dontBlockReading = false;
316 int result = read_socket(kdeinitSocket, (char *) &request_header,
317 sizeof( request_header));
318 if (result == -1)
320 kDebug(7016) << "Exiting on read_socket errno:" << errno;
321 KDE_signal( SIGHUP, SIG_IGN);
322 KDE_signal( SIGTERM, SIG_IGN);
323 destruct(); // Exit!
325 requestData.resize(request_header.arg_length);
326 result = read_socket(kdeinitSocket, (char *) requestData.data(),
327 request_header.arg_length);
329 processRequestReturn(request_header.cmd,requestData);
332 #endif
334 void KLauncher::processRequestReturn(int status, const QByteArray &requestData)
336 if (status == LAUNCHER_CHILD_DIED)
338 long *request_data;
339 request_data = (long *) requestData.data();
340 processDied(request_data[0], request_data[1]);
341 return;
343 if (lastRequest && (status == LAUNCHER_OK))
345 long *request_data;
346 request_data = (long *) requestData.data();
347 lastRequest->pid = (pid_t) (*request_data);
348 kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid <<
349 ") up and running.";
350 switch(lastRequest->dbus_startup_type)
352 case KService::DBusNone:
353 lastRequest->status = KLaunchRequest::Running;
354 break;
355 case KService::DBusUnique:
356 case KService::DBusWait:
357 case KService::DBusMulti:
358 lastRequest->status = KLaunchRequest::Launching;
359 break;
361 lastRequest = 0;
362 return;
364 if (lastRequest && (status == LAUNCHER_ERROR))
366 lastRequest->status = KLaunchRequest::Error;
367 kDebug(7016) << lastRequest->name << " failed." << endl;
368 if (!requestData.isEmpty())
369 lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data());
370 lastRequest = 0;
371 return;
374 kWarning(7016)<< "Unexpected request return" << (unsigned int) status;
377 void
378 KLauncher::processDied(pid_t pid, long exitStatus)
380 #ifdef KLAUNCHER_VERBOSE_OUTPUT
381 kDebug(7016) << pid << "exitStatus=" << exitStatus;
382 #else
383 Q_UNUSED(exitStatus);
384 // We should probably check the exitStatus for the uniqueapp case?
385 #endif
386 foreach (KLaunchRequest *request, requestList)
388 #ifdef KLAUNCHER_VERBOSE_OUTPUT
389 kDebug(7016) << " had pending request" << request->pid;
390 #endif
391 if (request->pid == pid)
393 if (request->dbus_startup_type == KService::DBusWait)
394 request->status = KLaunchRequest::Done;
395 else if ((request->dbus_startup_type == KService::DBusUnique)
396 && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name))
397 request->status = KLaunchRequest::Running;
398 else
399 request->status = KLaunchRequest::Error;
400 #ifdef KLAUNCHER_VERBOSE_OUTPUT
401 kDebug(7016) << pid << "died, requestDone. status=" << request->status;
402 #endif
403 requestDone(request);
404 return;
407 #ifdef KLAUNCHER_VERBOSE_OUTPUT
408 kDebug(7016) << "found no pending requests for PID" << pid;
409 #endif
412 static bool matchesPendingRequest(const QString& appId, const QString& pendingAppId)
414 // appId just registered, e.g. org.koffice.kword-12345
415 // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for.
417 const QString newAppId = appId.left(appId.lastIndexOf('-')); // strip out the -12345 if present.
419 //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId;
421 if (pendingAppId.startsWith("*.")) {
422 const QString pendingName = pendingAppId.mid(2);
423 const QString appName = newAppId.mid(newAppId.lastIndexOf('.')+1);
424 //kDebug() << "appName=" << appName;
425 return appName == pendingName;
428 return newAppId == pendingAppId;
431 void
432 KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner,
433 const QString &newOwner)
435 Q_UNUSED(oldOwner);
436 if (appId.isEmpty() || newOwner.isEmpty())
437 return;
439 #ifdef KLAUNCHER_VERBOSE_OUTPUT
440 kDebug(7016) << "new app" << appId;
441 #endif
442 foreach (KLaunchRequest *request, requestList)
444 if (request->status != KLaunchRequest::Launching)
445 continue;
447 // For unique services check the requested service name first
448 if ((request->dbus_startup_type == KService::DBusUnique) &&
449 ((appId == request->dbus_name) ||
450 QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)))
452 #ifdef KLAUNCHER_VERBOSE_OUTPUT
453 kDebug(7016) << "ok, request (for unique app) done";
454 #endif
455 request->status = KLaunchRequest::Running;
456 requestDone(request);
457 continue;
460 const QString rAppId = request->dbus_name;
461 #ifdef KLAUNCHER_VERBOSE_OUTPUT
462 kDebug(7016) << "had pending request" << rAppId;
463 #endif
464 if (rAppId.isEmpty())
465 continue;
467 if (matchesPendingRequest(appId, rAppId)) {
468 #ifdef KLAUNCHER_VERBOSE_OUTPUT
469 kDebug(7016) << "ok, request done";
470 #endif
471 request->dbus_name = appId;
472 request->status = KLaunchRequest::Running;
473 requestDone(request);
474 continue;
479 void
480 KLauncher::autoStart(int phase)
482 if( mAutoStart.phase() >= phase )
483 return;
484 mAutoStart.setPhase(phase);
485 if (phase == 0)
486 mAutoStart.loadAutoStartList();
487 mAutoTimer.start(0);
490 void
491 KLauncher::slotAutoStart()
493 KService::Ptr s;
496 QString service = mAutoStart.startService();
497 if (service.isEmpty())
499 // Done
500 if( !mAutoStart.phaseDone())
502 mAutoStart.setPhaseDone();
503 switch( mAutoStart.phase())
505 case 0:
506 emit autoStart0Done();
507 break;
508 case 1:
509 emit autoStart1Done();
510 break;
511 case 2:
512 emit autoStart2Done();
513 break;
516 return;
518 s = new KService(service);
520 while (!start_service(s, QStringList(), QStringList(), "0", false, true, QDBusMessage()));
521 // Loop till we find a service that we can start.
524 void
525 KLauncher::requestDone(KLaunchRequest *request)
527 if ((request->status == KLaunchRequest::Running) ||
528 (request->status == KLaunchRequest::Done))
530 requestResult.result = 0;
531 requestResult.dbusName = request->dbus_name;
532 requestResult.error = "";
533 requestResult.pid = request->pid;
535 else
537 requestResult.result = 1;
538 requestResult.dbusName = "";
539 requestResult.error = i18n("KDEInit could not launch '%1'.", request->name);
540 if (!request->errorMsg.isEmpty())
541 requestResult.error += ":\n" + request->errorMsg;
542 requestResult.pid = 0;
544 #ifdef Q_WS_X11
545 if (!request->startup_dpy.isEmpty())
547 Display* dpy = NULL;
548 if( (mCached_dpy != NULL) &&
549 (request->startup_dpy == XDisplayString( mCached_dpy )))
550 dpy = mCached_dpy;
551 if( dpy == NULL )
552 dpy = XOpenDisplay( request->startup_dpy.toLocal8Bit() );
553 if( dpy )
555 KStartupInfoId id;
556 id.initId( request->startup_id.toLocal8Bit() );
557 KStartupInfo::sendFinishX( dpy, id );
558 if( mCached_dpy != dpy && mCached_dpy != NULL )
559 XCloseDisplay( mCached_dpy );
560 mCached_dpy = dpy;
563 #endif
566 if (request->autoStart)
568 mAutoTimer.start(0);
571 if (request->transaction.type() != QDBusMessage::InvalidMessage)
573 if ( requestResult.dbusName.isNull() ) // null strings can't be sent
574 requestResult.dbusName = "";
575 Q_ASSERT( !requestResult.error.isNull() );
576 PIDType<sizeof(pid_t)>::PID_t stream_pid = requestResult.pid;
577 QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result
578 << requestResult.dbusName
579 << requestResult.error
580 << stream_pid));
582 #ifdef KLAUNCHER_VERBOSE_OUTPUT
583 kDebug(7016) << "removing done request" << request->name << "PID" << request->pid;
584 #endif
586 requestList.removeAll( request );
587 delete request;
590 static void appendLong(QByteArray &ba, long l)
592 const int sz = ba.size();
593 ba.resize(sz + sizeof(long));
594 memcpy(ba.data() + sz, &l, sizeof(long));
597 void
598 KLauncher::requestStart(KLaunchRequest *request)
600 #ifdef Q_WS_WIN
601 requestList.append( request );
602 lastRequest = request;
604 KProcess *process = new KProcess;
605 process->setOutputChannelMode(KProcess::MergedChannels);
606 connect(process ,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) );
607 connect(process ,SIGNAL(finished(int, QProcess::ExitStatus)),this, SLOT(slotFinished(int, QProcess::ExitStatus)) );
608 request->process = process;
610 // process.setEnvironment(envlist);
611 QStringList args;
612 foreach (const QString &arg, request->arg_list)
613 args << arg;
615 process->setProgram(request->name,args);
616 process->start();
618 if (!process->waitForStarted())
620 processRequestReturn(LAUNCHER_ERROR,"");
622 else
624 request->pid = process->pid();
625 QByteArray data((char *)&request->pid, sizeof(int));
626 processRequestReturn(LAUNCHER_OK,data);
628 return;
630 #else
631 requestList.append( request );
632 // Send request to kdeinit.
633 klauncher_header request_header;
634 QByteArray requestData;
635 requestData.reserve(1024);
637 appendLong(requestData, request->arg_list.count() + 1);
638 requestData.append(request->name.toLocal8Bit());
639 requestData.append('\0');
640 foreach (const QString &arg, request->arg_list)
641 requestData.append(arg.toLocal8Bit()).append('\0');
642 appendLong(requestData, request->envs.count());
643 foreach (const QString &env, request->envs)
644 requestData.append(env.toLocal8Bit()).append('\0');
645 appendLong(requestData, 0); // avoid_loops, always false here
646 #ifdef Q_WS_X11
647 bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0";
648 if( startup_notify )
649 requestData.append(request->startup_id.toLocal8Bit()).append('\0');
650 #endif
651 if (!request->cwd.isEmpty())
652 requestData.append(request->cwd.toLocal8Bit()).append('\0');
654 #ifdef Q_WS_X11
655 request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW;
656 #else
657 request_header.cmd = LAUNCHER_EXEC_NEW;
658 #endif
659 request_header.arg_length = requestData.length();
660 write(kdeinitSocket, &request_header, sizeof(request_header));
661 write(kdeinitSocket, requestData.data(), requestData.length());
663 // Wait for pid to return.
664 lastRequest = request;
665 dontBlockReading = false;
666 do {
667 slotKDEInitData( kdeinitSocket );
669 while (lastRequest != 0);
670 dontBlockReading = true;
671 #endif
674 void KLauncher::exec_blind(const QString &name, const QStringList &arg_list, const QStringList &envs, const QString &startup_id)
676 KLaunchRequest *request = new KLaunchRequest;
677 request->autoStart = false;
678 request->name = name;
679 request->arg_list = arg_list;
680 request->dbus_startup_type = KService::DBusNone;
681 request->pid = 0;
682 request->status = KLaunchRequest::Launching;
683 request->envs = envs;
684 // Find service, if any - strip path if needed
685 KService::Ptr service = KService::serviceByDesktopName( name.mid( name.lastIndexOf( '/' ) + 1 ));
686 if (service)
687 send_service_startup_info( request, service, startup_id, QStringList());
688 else // no .desktop file, no startup info
689 cancel_service_startup_info( request, startup_id, envs );
691 requestStart(request);
692 // We don't care about this request any longer....
693 requestDone(request);
697 // KDE5: remove
698 bool
699 KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls,
700 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
702 KService::Ptr service;
703 // Find service
704 service = KService::serviceByName(serviceName);
705 if (!service)
707 requestResult.result = ENOENT;
708 requestResult.error = i18n("Could not find service '%1'.", serviceName);
709 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
710 return false;
712 return start_service(service, urls, envs, startup_id, blind, false, msg);
715 bool
716 KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls,
717 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
719 KService::Ptr service;
720 // Find service
721 if (QFileInfo(serviceName).isAbsolute() )
723 // Full path
724 service = new KService(serviceName);
726 else
728 service = KService::serviceByDesktopPath(serviceName);
730 if (!service)
732 requestResult.result = ENOENT;
733 requestResult.error = i18n("Could not find service '%1'.", serviceName);
734 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
735 return false;
737 return start_service(service, urls, envs, startup_id, blind, false, msg);
740 bool
741 KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls,
742 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
744 KService::Ptr service = KService::serviceByDesktopName(serviceName);
745 if (!service)
747 requestResult.result = ENOENT;
748 requestResult.error = i18n("Could not find service '%1'.", serviceName);
749 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
750 return false;
752 return start_service(service, urls, envs, startup_id, blind, false, msg);
755 bool
756 KLauncher::start_service(KService::Ptr service, const QStringList &_urls,
757 const QStringList &envs, const QString &startup_id,
758 bool blind, bool autoStart, const QDBusMessage &msg)
760 QStringList urls = _urls;
761 if (!service->isValid())
763 requestResult.result = ENOEXEC;
764 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
765 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
766 return false;
768 KLaunchRequest *request = new KLaunchRequest;
769 request->autoStart = autoStart;
771 if ((urls.count() > 1) && !service->allowMultipleFiles())
773 // We need to launch the application N times. That sucks.
774 // We ignore the result for application 2 to N.
775 // For the first file we launch the application in the
776 // usual way. The reported result is based on this
777 // application.
778 QStringList::ConstIterator it = urls.constBegin();
779 for(++it;
780 it != urls.constEnd();
781 ++it)
783 QStringList singleUrl;
784 singleUrl.append(*it);
785 QString startup_id2 = startup_id;
786 if( !startup_id2.isEmpty() && startup_id2 != "0" )
787 startup_id2 = "0"; // can't use the same startup_id several times
788 start_service( service, singleUrl, envs, startup_id2, true, false, msg);
790 QString firstURL = *(urls.begin());
791 urls.clear();
792 urls.append(firstURL);
794 createArgs(request, service, urls);
796 // We must have one argument at least!
797 if (!request->arg_list.count())
799 requestResult.result = ENOEXEC;
800 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
801 delete request;
802 cancel_service_startup_info( NULL, startup_id, envs );
803 return false;
806 request->name = request->arg_list.takeFirst();
808 if (request->name.endsWith("/kioexec")) {
809 // Special case for kioexec; if createArgs said we were going to use it,
810 // then we have to expect a kioexec-PID, not a org.kde.finalapp...
811 // Testcase: konqueror www.kde.org, RMB on link, open with, kruler.
813 request->dbus_startup_type = KService::DBusMulti;
814 request->dbus_name = "org.kde.kioexec";
815 } else {
816 request->dbus_startup_type = service->dbusStartupType();
818 if ((request->dbus_startup_type == KService::DBusUnique) ||
819 (request->dbus_startup_type == KService::DBusMulti)) {
820 const QVariant v = service->property("X-DBUS-ServiceName");
821 if (v.isValid()) {
822 request->dbus_name = v.toString().toUtf8();
824 if (request->dbus_name.isEmpty()) {
825 request->dbus_name = "*." + QFile::encodeName(KRun::binaryName(service->exec(), true));
830 request->pid = 0;
831 request->envs = envs;
832 send_service_startup_info( request, service, startup_id, envs );
834 // Request will be handled later.
835 if (!blind && !autoStart)
837 msg.setDelayedReply(true);
838 request->transaction = msg;
840 queueRequest(request);
841 return true;
844 void
845 KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QString& startup_id,
846 const QStringList &envs )
848 #ifdef Q_WS_X11
849 request->startup_id = "0";
850 if( startup_id == "0" )
851 return;
852 bool silent;
853 QByteArray wmclass;
854 if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass ))
855 return;
856 KStartupInfoId id;
857 id.initId( startup_id.toLatin1() );
858 QString dpy_str;
859 foreach (const QString &env, envs) {
860 if (env.startsWith(QLatin1String("DISPLAY=")))
861 dpy_str = env.mid(8);
863 Display* dpy = NULL;
864 if( !dpy_str.isEmpty() && mCached_dpy != NULL
865 && dpy_str != QLatin1String(XDisplayString(mCached_dpy)) )
866 dpy = mCached_dpy;
867 if( dpy == NULL )
868 dpy = XOpenDisplay( dpy_str.toLatin1().constData() );
869 request->startup_id = id.id();
870 if( dpy == NULL )
872 cancel_service_startup_info( request, startup_id, envs );
873 return;
876 request->startup_dpy = dpy_str;
878 KStartupInfoData data;
879 data.setName( service->name());
880 data.setIcon( service->icon());
881 data.setDescription( i18n( "Launching %1" , service->name()));
882 if( !wmclass.isEmpty())
883 data.setWMClass( wmclass );
884 if( silent )
885 data.setSilent( KStartupInfoData::Yes );
886 // the rest will be sent by kdeinit
887 KStartupInfo::sendStartupX( dpy, id, data );
888 if( mCached_dpy != dpy && mCached_dpy != NULL )
889 XCloseDisplay( mCached_dpy );
890 mCached_dpy = dpy;
891 return;
892 #else
893 return;
894 #endif
897 void
898 KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QString& startup_id,
899 const QStringList &envs )
901 #ifdef Q_WS_X11
902 if( request != NULL )
903 request->startup_id = "0";
904 if( !startup_id.isEmpty() && startup_id != "0" )
906 QString dpy_str;
907 foreach (const QString &env, envs) {
908 if (env.startsWith(QLatin1String("DISPLAY=")))
909 dpy_str = env.mid(8);
911 Display* dpy = NULL;
912 if( !dpy_str.isEmpty() && mCached_dpy != NULL
913 && dpy_str != QLatin1String(XDisplayString( mCached_dpy )) )
914 dpy = mCached_dpy;
915 if( dpy == NULL )
916 dpy = XOpenDisplay( dpy_str.toLatin1().constData() );
917 if( dpy == NULL )
918 return;
919 KStartupInfoId id;
920 id.initId( startup_id.toLatin1() );
921 KStartupInfo::sendFinishX( dpy, id );
922 if( mCached_dpy != dpy && mCached_dpy != NULL )
923 XCloseDisplay( mCached_dpy );
924 mCached_dpy = dpy;
926 #endif
929 bool
930 KLauncher::kdeinit_exec(const QString &app, const QStringList &args,
931 const QString& workdir, const QStringList &envs,
932 const QString &startup_id, bool wait, const QDBusMessage &msg)
934 KLaunchRequest *request = new KLaunchRequest;
935 request->autoStart = false;
937 for(QStringList::ConstIterator it = args.begin();
938 it != args.end();
939 ++it)
941 QString arg = *it;
942 request->arg_list.append(arg.toLocal8Bit());
945 request->name = app.toLocal8Bit();
947 if (wait)
948 request->dbus_startup_type = KService::DBusWait;
949 else
950 request->dbus_startup_type = KService::DBusNone;
951 request->pid = 0;
952 #ifdef Q_WS_X11
953 request->startup_id = startup_id;
954 #endif
955 request->envs = envs;
956 request->cwd = workdir;
957 if( !app.endsWith("kbuildsycoca4") ) // avoid stupid loop
959 // Find service, if any - strip path if needed
960 KService::Ptr service = KService::serviceByDesktopName( app.mid( app.lastIndexOf( '/' ) + 1 ));
961 if (service)
962 send_service_startup_info( request, service,
963 startup_id, QStringList());
964 else // no .desktop file, no startup info
965 cancel_service_startup_info( request, startup_id, envs );
967 msg.setDelayedReply(true);
968 request->transaction = msg;
969 queueRequest(request);
970 return true;
973 void
974 KLauncher::queueRequest(KLaunchRequest *request)
976 requestQueue.append( request );
977 if (!bProcessingQueue)
979 bProcessingQueue = true;
980 QTimer::singleShot(0, this, SLOT( slotDequeue() ));
984 void
985 KLauncher::slotDequeue()
987 do {
988 KLaunchRequest *request = requestQueue.takeFirst();
989 // process request
990 request->status = KLaunchRequest::Launching;
991 requestStart(request);
992 if (request->status != KLaunchRequest::Launching)
994 // Request handled.
995 #ifdef KLAUNCHER_VERBOSE_OUTPUT
996 kDebug(7016) << "Request handled already";
997 #endif
998 requestDone( request );
999 continue;
1001 } while(requestQueue.count());
1002 bProcessingQueue = false;
1005 void
1006 KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service ,
1007 const QStringList &urls)
1009 const QStringList params = KRun::processDesktopExec(*service, urls);
1011 for(QStringList::ConstIterator it = params.begin();
1012 it != params.end(); ++it)
1014 request->arg_list.append(*it);
1016 request->cwd = service->path();
1019 ///// IO-Slave functions
1021 pid_t
1022 KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket)
1024 IdleSlave *slave = 0;
1025 foreach (IdleSlave *p, mSlaveList)
1027 if (p->onHold(url))
1029 slave = p;
1030 break;
1033 if (slave)
1035 mSlaveList.removeAll(slave);
1036 slave->connect(app_socket);
1037 return slave->pid();
1039 return 0;
1043 pid_t
1044 KLauncher::requestSlave(const QString &protocol,
1045 const QString &host,
1046 const QString &app_socket,
1047 QString &error)
1049 IdleSlave *slave = 0;
1050 foreach (IdleSlave *p, mSlaveList)
1052 if (p->match(protocol, host, true))
1054 slave = p;
1055 break;
1058 if (!slave)
1060 foreach (IdleSlave *p, mSlaveList)
1062 if (p->match(protocol, host, false))
1064 slave = p;
1065 break;
1069 if (!slave)
1071 foreach (IdleSlave *p, mSlaveList)
1073 if (p->match(protocol, QString(), false))
1075 slave = p;
1076 break;
1080 if (slave)
1082 mSlaveList.removeAll(slave);
1083 slave->connect(app_socket);
1084 return slave->pid();
1087 QString name = KProtocolInfo::exec(protocol);
1088 if (name.isEmpty())
1090 error = i18n("Unknown protocol '%1'.\n", protocol);
1091 return 0;
1094 QStringList arg_list;
1095 #ifdef Q_WS_WIN
1096 arg_list << name;
1097 arg_list << protocol;
1098 arg_list << mConnectionServer.address();
1099 arg_list << app_socket;
1100 name = KStandardDirs::findExe("kioslave");
1101 #else
1102 QString arg1 = protocol;
1103 QString arg2 = mConnectionServer.address();
1104 QString arg3 = app_socket;
1105 arg_list.append(arg1);
1106 arg_list.append(arg2);
1107 arg_list.append(arg3);
1108 #endif
1110 kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol
1111 << " args=" << arg_list << endl;
1113 #ifdef Q_OS_UNIX
1114 if (mSlaveDebug == arg1)
1116 klauncher_header request_header;
1117 request_header.cmd = LAUNCHER_DEBUG_WAIT;
1118 request_header.arg_length = 0;
1119 write(kdeinitSocket, &request_header, sizeof(request_header));
1121 if (mSlaveValgrind == arg1)
1123 arg_list.prepend(QFile::encodeName(KLibLoader::findLibrary(name.toLocal8Bit())));
1124 arg_list.prepend(QFile::encodeName(KStandardDirs::locate("exe", "kioslave")));
1125 name = "valgrind";
1126 if (!mSlaveValgrindSkin.isEmpty()) {
1127 arg_list.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin);
1128 } else
1129 arg_list.prepend(QLatin1String("--tool=memcheck"));
1131 #endif
1132 KLaunchRequest *request = new KLaunchRequest;
1133 request->autoStart = false;
1134 request->name = name;
1135 request->arg_list = arg_list;
1136 request->dbus_startup_type = KService::DBusNone;
1137 request->pid = 0;
1138 #ifdef Q_WS_X11
1139 request->startup_id = "0";
1140 #endif
1141 request->status = KLaunchRequest::Launching;
1142 requestStart(request);
1143 pid_t pid = request->pid;
1145 // kDebug(7016) << "Slave launched, pid = " << pid;
1147 // We don't care about this request any longer....
1148 requestDone(request);
1149 if (!pid)
1151 error = i18n("Error loading '%1'.\n", name);
1153 return pid;
1156 void
1157 KLauncher::waitForSlave(int pid, const QDBusMessage &msg)
1159 foreach (IdleSlave *slave, mSlaveList)
1161 if (slave->pid() == static_cast<pid_t>(pid))
1162 return; // Already here.
1164 SlaveWaitRequest *waitRequest = new SlaveWaitRequest;
1165 msg.setDelayedReply(true);
1166 waitRequest->transaction = msg;
1167 waitRequest->pid = static_cast<pid_t>(pid);
1168 mSlaveWaitRequest.append(waitRequest);
1171 void
1172 KLauncher::acceptSlave()
1174 IdleSlave *slave = new IdleSlave(this);
1175 mConnectionServer.setNextPendingConnection(&slave->mConn);
1176 mSlaveList.append(slave);
1177 connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone()));
1178 connect(slave, SIGNAL(statusUpdate(IdleSlave *)),
1179 this, SLOT(slotSlaveStatus(IdleSlave *)));
1180 if (!mTimer.isActive())
1182 mTimer.start(1000*10);
1186 void
1187 KLauncher::slotSlaveStatus(IdleSlave *slave)
1189 QMutableListIterator<SlaveWaitRequest *> it(mSlaveWaitRequest);
1190 while(it.hasNext())
1192 SlaveWaitRequest *waitRequest = it.next();
1193 if (waitRequest->pid == slave->pid())
1195 QDBusConnection::sessionBus().send(waitRequest->transaction.createReply());
1196 it.remove();
1197 delete waitRequest;
1202 void
1203 KLauncher::slotSlaveGone()
1205 IdleSlave *slave = (IdleSlave *) sender();
1206 mSlaveList.removeAll(slave);
1207 if ((mSlaveList.count() == 0) && (mTimer.isActive()))
1209 mTimer.stop();
1213 void
1214 KLauncher::idleTimeout()
1216 bool keepOneFileSlave=true;
1217 time_t now = time(0);
1218 foreach (IdleSlave *slave, mSlaveList)
1220 if ((slave->protocol()=="file") && (keepOneFileSlave))
1221 keepOneFileSlave=false;
1222 else if (slave->age(now) > SLAVE_MAX_IDLE)
1224 // killing idle slave
1225 delete slave;
1230 void KLauncher::reparseConfiguration()
1232 KProtocolManager::reparseConfiguration();
1233 foreach (IdleSlave *slave, mSlaveList)
1234 slave->reparseConfiguration();
1237 #ifdef Q_WS_WIN
1238 void
1239 KLauncher::slotGotOutput()
1241 KProcess *p = static_cast<KProcess *>(sender());
1242 QByteArray _stdout = p->readAllStandardOutput();
1243 kDebug(7016) << _stdout.data();
1246 void
1247 KLauncher::slotFinished(int exitCode, QProcess::ExitStatus exitStatus )
1249 KProcess *p = static_cast<KProcess *>(sender());
1250 kDebug(7016) << "process finished exitcode=" << exitCode << "exitStatus=" << exitStatus;
1252 foreach (KLaunchRequest *request, requestList)
1254 if (request->process == p)
1256 #ifdef KLAUNCHER_VERBOSE_OUTPUT
1257 kDebug(7016) << "found KProcess, request done";
1258 #endif
1259 if (exitCode == 0 && exitStatus == QProcess::NormalExit)
1260 request->status = KLaunchRequest::Done;
1261 else
1262 request->status = KLaunchRequest::Error;
1263 requestDone(request);
1264 request->process = 0;
1267 delete p;
1269 #endif
1271 #include "klauncher.moc"