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 #define QT_NO_CAST_FROM_ASCII
22 #include "klauncher.h"
23 #include "klauncher_cmds.h"
24 #include "klauncher_adaptor.h"
36 #include <kstartupinfo.h>
40 #include <QtCore/QFile>
45 #include <klibloader.h>
47 #include <kprotocolmanager.h>
48 #include <kprotocolinfo.h>
50 #include <kstandarddirs.h>
51 #include <ktemporaryfile.h>
52 #include <kdesktopfile.h>
55 #include <kio/global.h>
56 #include <kio/connection.h>
57 #include <kio/slaveinterface.h>
59 // Dispose slaves after being idle for SLAVE_MAX_IDLE seconds
60 #define SLAVE_MAX_IDLE 30
62 // #define KLAUNCHER_VERBOSE_OUTPUT
64 static const char* const s_DBusStartupTypeToString
[] =
65 { "DBusNone", "DBusUnique", "DBusMulti", "DBusWait", "ERROR" };
69 IdleSlave::IdleSlave(QObject
*parent
)
72 QObject::connect(&mConn
, SIGNAL(readyRead()), this, SLOT(gotInput()));
73 // Send it a SLAVE_STATUS command.
74 mConn
.send( CMD_SLAVE_STATUS
);
80 template<int T
> struct PIDType
{ typedef pid_t PID_t
; } ;
81 template<> struct PIDType
<2> { typedef qint16 PID_t
; } ;
82 template<> struct PIDType
<4> { typedef qint32 PID_t
; } ;
89 if (mConn
.read( &cmd
, data
) == -1)
91 // Communication problem with slave.
92 kError(7016) << "SlavePool: No communication with slave." << endl
;
95 else if (cmd
== MSG_SLAVE_ACK
)
99 else if (cmd
!= MSG_SLAVE_STATUS
)
101 kError(7016) << "SlavePool: Unexpected data from slave." << endl
;
106 QDataStream
stream( data
);
107 PIDType
<sizeof(pid_t
)>::PID_t stream_pid
;
112 stream
>> stream_pid
>> protocol
>> host
>> b
;
114 // Overload with (bool) onHold, (KUrl) url.
124 mConnected
= (b
!= 0);
125 mProtocol
= QString::fromLatin1(protocol
);
127 emit
statusUpdate(this);
132 IdleSlave::connect(const QString
&app_socket
)
135 QDataStream
stream( &data
, QIODevice::WriteOnly
);
136 stream
<< app_socket
;
137 mConn
.send( CMD_SLAVE_CONNECT
, data
);
142 IdleSlave::reparseConfiguration()
144 mConn
.send( CMD_REPARSECONFIGURATION
);
148 IdleSlave::match(const QString
&protocol
, const QString
&host
, bool needConnected
)
150 if (mOnHold
|| protocol
!= mProtocol
) {
153 if (host
.isEmpty()) {
156 return (host
== mHost
) && (!needConnected
|| mConnected
);
160 IdleSlave::onHold(const KUrl
&url
)
162 if (!mOnHold
) return false;
163 return (url
== mUrl
);
167 IdleSlave::age(time_t now
)
169 return (int) difftime(now
, mBirthDate
);
172 static KLauncher
* g_klauncher_self
;
174 KLauncher::KLauncher(int _kdeinitSocket
)
176 kdeinitSocket(_kdeinitSocket
)
181 Q_ASSERT( g_klauncher_self
== NULL
);
182 g_klauncher_self
= this;
184 mAutoTimer
.setSingleShot(true);
185 new KLauncherAdaptor(this);
186 QDBusConnection::sessionBus().registerObject(QLatin1String("/KLauncher"), this); // same as ktoolinvocation.cpp
188 connect(&mAutoTimer
, SIGNAL(timeout()), this, SLOT(slotAutoStart()));
189 connect(QDBusConnection::sessionBus().interface(),
190 SIGNAL(serviceOwnerChanged(QString
,QString
,QString
)),
191 SLOT(slotNameOwnerChanged(QString
,QString
,QString
)));
193 mConnectionServer
.listenForRemote();
194 connect(&mConnectionServer
, SIGNAL(newConnection()), SLOT(acceptSlave()));
195 if (!mConnectionServer
.isListening())
198 qDebug("KLauncher: Fatal error, can't create tempfile!");
202 connect(&mTimer
, SIGNAL(timeout()), SLOT(idleTimeout()));
205 kdeinitNotifier
= new QSocketNotifier(kdeinitSocket
, QSocketNotifier::Read
);
206 connect(kdeinitNotifier
, SIGNAL( activated( int )),
207 this, SLOT( slotKDEInitData( int )));
208 kdeinitNotifier
->setEnabled( true );
211 bProcessingQueue
= false;
213 mSlaveDebug
= QString::fromLocal8Bit(qgetenv("KDE_SLAVE_DEBUG_WAIT"));
214 if (!mSlaveDebug
.isEmpty())
216 qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", qPrintable(mSlaveDebug
));
218 mSlaveValgrind
= QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND"));
219 if (!mSlaveValgrind
.isEmpty())
221 mSlaveValgrindSkin
= QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND_SKIN"));
222 qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", qPrintable(mSlaveValgrind
));
225 kDebug(7016) << "LAUNCHER_OK";
227 klauncher_header request_header
;
228 request_header
.cmd
= LAUNCHER_OK
;
229 request_header
.arg_length
= 0;
230 write(kdeinitSocket
, &request_header
, sizeof(request_header
));
234 KLauncher::~KLauncher()
237 g_klauncher_self
= NULL
;
240 void KLauncher::close()
243 if( mCached_dpy
!= NULL
)
245 XCloseDisplay( mCached_dpy
);
252 KLauncher::destruct()
254 if (g_klauncher_self
)
255 g_klauncher_self
->close();
256 // We don't delete the app here, that's intentional.
260 void KLauncher::setLaunchEnv(const QString
&name
, const QString
&value
)
265 klauncher_header request_header
;
266 QByteArray requestData
;
267 requestData
.append(name
.toLocal8Bit()).append('\0').append(value
.toLocal8Bit()).append('\0');
268 request_header
.cmd
= LAUNCHER_SETENV
;
269 request_header
.arg_length
= requestData
.size();
270 write(kdeinitSocket
, &request_header
, sizeof(request_header
));
271 write(kdeinitSocket
, requestData
.data(), request_header
.arg_length
);
277 * Read 'len' bytes from 'sock' into buffer.
278 * returns -1 on failure, 0 on no data.
281 read_socket(int sock
, char *buffer
, int len
)
284 int bytes_left
= len
;
285 while (bytes_left
> 0) {
286 // in case we get a request to start an application and data arrive
287 // to kdeinitSocket at the same time, requestStart() will already
288 // call slotKDEInitData(), so we must check there's still something
289 // to read, otherwise this would block
291 // Same thing if kdeinit dies without warning.
294 timeval tm
= { 30, 0 }; // 30 seconds timeout, so we're not stuck in case kdeinit dies on us
297 select( sock
+ 1, &in
, 0, 0, &tm
);
298 if( !FD_ISSET( sock
, &in
)) {
299 kDebug(7016) << "read_socket" << sock
<< "nothing to read, kdeinit4 must be dead";
303 result
= read(sock
, buffer
, bytes_left
);
307 bytes_left
-= result
;
309 else if (result
== 0)
311 else if ((result
== -1) && (errno
!= EINTR
))
319 KLauncher::slotKDEInitData(int)
321 klauncher_header request_header
;
322 QByteArray requestData
;
324 int result
= read_socket(kdeinitSocket
, (char *) &request_header
,
325 sizeof( request_header
));
328 kDebug(7016) << "Exiting on read_socket errno:" << errno
;
329 KDE_signal( SIGHUP
, SIG_IGN
);
330 KDE_signal( SIGTERM
, SIG_IGN
);
333 requestData
.resize(request_header
.arg_length
);
334 result
= read_socket(kdeinitSocket
, (char *) requestData
.data(),
335 request_header
.arg_length
);
337 processRequestReturn(request_header
.cmd
,requestData
);
342 void KLauncher::processRequestReturn(int status
, const QByteArray
&requestData
)
344 if (status
== LAUNCHER_CHILD_DIED
)
347 request_data
= (long *) requestData
.data();
348 processDied(request_data
[0], request_data
[1]);
351 if (lastRequest
&& (status
== LAUNCHER_OK
))
354 request_data
= (long *) requestData
.data();
355 lastRequest
->pid
= (pid_t
) (*request_data
);
356 kDebug(7016).nospace() << lastRequest
->name
<< " (pid " << lastRequest
->pid
<<
358 switch(lastRequest
->dbus_startup_type
)
360 case KService::DBusNone
:
361 lastRequest
->status
= KLaunchRequest::Running
;
363 case KService::DBusUnique
:
364 case KService::DBusWait
:
365 case KService::DBusMulti
:
366 lastRequest
->status
= KLaunchRequest::Launching
;
372 if (lastRequest
&& (status
== LAUNCHER_ERROR
))
374 lastRequest
->status
= KLaunchRequest::Error
;
375 kDebug(7016) << lastRequest
->name
<< " failed." << endl
;
376 if (!requestData
.isEmpty())
377 lastRequest
->errorMsg
= QString::fromUtf8((char *) requestData
.data());
382 kWarning(7016)<< "Unexpected request return" << (unsigned int) status
;
386 KLauncher::processDied(pid_t pid
, long exitStatus
)
388 #ifdef KLAUNCHER_VERBOSE_OUTPUT
389 kDebug(7016) << pid
<< "exitStatus=" << exitStatus
;
391 Q_UNUSED(exitStatus
);
392 // We should probably check the exitStatus for the uniqueapp case?
394 foreach (KLaunchRequest
*request
, requestList
)
396 #ifdef KLAUNCHER_VERBOSE_OUTPUT
397 kDebug(7016) << " had pending request" << request
->pid
;
399 if (request
->pid
== pid
)
401 if (request
->dbus_startup_type
== KService::DBusWait
)
402 request
->status
= KLaunchRequest::Done
;
403 else if ((request
->dbus_startup_type
== KService::DBusUnique
)
404 && QDBusConnection::sessionBus().interface()->isServiceRegistered(request
->dbus_name
)) {
405 request
->status
= KLaunchRequest::Running
;
406 #ifdef KLAUNCHER_VERBOSE_OUTPUT
407 kDebug(7016) << pid
<< "running as a unique app";
410 request
->status
= KLaunchRequest::Error
;
411 #ifdef KLAUNCHER_VERBOSE_OUTPUT
412 kDebug(7016) << pid
<< "died, requestDone. status=" << request
->status
;
415 requestDone(request
);
419 #ifdef KLAUNCHER_VERBOSE_OUTPUT
420 kDebug(7016) << "found no pending requests for PID" << pid
;
424 static bool matchesPendingRequest(const QString
& appId
, const QString
& pendingAppId
)
426 // appId just registered, e.g. org.koffice.kword-12345
427 // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for.
429 const QString newAppId
= appId
.left(appId
.lastIndexOf(QLatin1Char('-'))); // strip out the -12345 if present.
431 //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId;
433 if (pendingAppId
.startsWith(QLatin1String("*."))) {
434 const QString pendingName
= pendingAppId
.mid(2);
435 const QString appName
= newAppId
.mid(newAppId
.lastIndexOf(QLatin1Char('.'))+1);
436 //kDebug() << "appName=" << appName;
437 return appName
== pendingName
;
440 return newAppId
== pendingAppId
;
444 KLauncher::slotNameOwnerChanged(const QString
&appId
, const QString
&oldOwner
,
445 const QString
&newOwner
)
448 if (appId
.isEmpty() || newOwner
.isEmpty())
451 #ifdef KLAUNCHER_VERBOSE_OUTPUT
452 kDebug(7016) << "new app" << appId
;
454 foreach (KLaunchRequest
*request
, requestList
)
456 if (request
->status
!= KLaunchRequest::Launching
)
459 #ifdef KLAUNCHER_VERBOSE_OUTPUT
460 kDebug(7016) << "had pending request" << request
->name
<< s_DBusStartupTypeToString
[request
->dbus_startup_type
] << "dbus_name" << request
->dbus_name
<< request
->tolerant_dbus_name
;
462 // For unique services check the requested service name first
463 if (request
->dbus_startup_type
== KService::DBusUnique
) {
464 if ((appId
== request
->dbus_name
) || // just started
465 QDBusConnection::sessionBus().interface()->isServiceRegistered(request
->dbus_name
)) { // was already running
466 request
->status
= KLaunchRequest::Running
;
467 #ifdef KLAUNCHER_VERBOSE_OUTPUT
468 kDebug(7016) << "OK, unique app" << request
->dbus_name
<< "is running";
470 requestDone(request
);
473 #ifdef KLAUNCHER_VERBOSE_OUTPUT
474 kDebug(7016) << "unique app" << request
->dbus_name
<< "not running yet";
479 const QString rAppId
= !request
->tolerant_dbus_name
.isEmpty() ? request
->tolerant_dbus_name
: request
->dbus_name
;
480 #ifdef KLAUNCHER_VERBOSE_OUTPUT
481 //kDebug(7016) << "using" << rAppId << "for matching";
483 if (rAppId
.isEmpty())
486 if (matchesPendingRequest(appId
, rAppId
)) {
487 #ifdef KLAUNCHER_VERBOSE_OUTPUT
488 kDebug(7016) << "ok, request done";
490 request
->dbus_name
= appId
;
491 request
->status
= KLaunchRequest::Running
;
492 requestDone(request
);
499 KLauncher::autoStart(int phase
)
501 if( mAutoStart
.phase() >= phase
)
503 mAutoStart
.setPhase(phase
);
505 mAutoStart
.loadAutoStartList();
510 KLauncher::slotAutoStart()
515 QString service
= mAutoStart
.startService();
516 if (service
.isEmpty())
519 if( !mAutoStart
.phaseDone())
521 mAutoStart
.setPhaseDone();
522 switch( mAutoStart
.phase())
525 emit
autoStart0Done();
528 emit
autoStart1Done();
531 emit
autoStart2Done();
537 s
= new KService(service
);
539 while (!start_service(s
, QStringList(), QStringList(), "0", false, true, QDBusMessage()));
540 // Loop till we find a service that we can start.
544 KLauncher::requestDone(KLaunchRequest
*request
)
546 if ((request
->status
== KLaunchRequest::Running
) ||
547 (request
->status
== KLaunchRequest::Done
))
549 requestResult
.result
= 0;
550 requestResult
.dbusName
= request
->dbus_name
;
551 requestResult
.error
= QString::fromLatin1(""); // not null, cf assert further down
552 requestResult
.pid
= request
->pid
;
556 requestResult
.result
= 1;
557 requestResult
.dbusName
= QString();
558 requestResult
.error
= i18n("KDEInit could not launch '%1'.", request
->name
);
559 if (!request
->errorMsg
.isEmpty())
560 requestResult
.error
+= QString::fromLatin1(":\n") + request
->errorMsg
;
561 requestResult
.pid
= 0;
564 if (!request
->startup_dpy
.isEmpty())
567 if( (mCached_dpy
!= NULL
) &&
568 (request
->startup_dpy
== XDisplayString( mCached_dpy
)))
571 dpy
= XOpenDisplay(request
->startup_dpy
);
575 id
.initId(request
->startup_id
);
576 KStartupInfo::sendFinishX( dpy
, id
);
577 if( mCached_dpy
!= dpy
&& mCached_dpy
!= NULL
)
578 XCloseDisplay( mCached_dpy
);
585 if (request
->autoStart
)
590 if (request
->transaction
.type() != QDBusMessage::InvalidMessage
)
592 if ( requestResult
.dbusName
.isNull() ) // null strings can't be sent
593 requestResult
.dbusName
= QString();
594 Q_ASSERT( !requestResult
.error
.isNull() );
595 PIDType
<sizeof(pid_t
)>::PID_t stream_pid
= requestResult
.pid
;
596 QDBusConnection::sessionBus().send(request
->transaction
.createReply(QVariantList() << requestResult
.result
597 << requestResult
.dbusName
598 << requestResult
.error
601 #ifdef KLAUNCHER_VERBOSE_OUTPUT
602 kDebug(7016) << "removing done request" << request
->name
<< "PID" << request
->pid
;
605 requestList
.removeAll( request
);
609 static void appendLong(QByteArray
&ba
, long l
)
611 const int sz
= ba
.size();
612 ba
.resize(sz
+ sizeof(long));
613 memcpy(ba
.data() + sz
, &l
, sizeof(long));
617 KLauncher::requestStart(KLaunchRequest
*request
)
620 requestList
.append( request
);
621 lastRequest
= request
;
623 KProcess
*process
= new KProcess
;
624 process
->setOutputChannelMode(KProcess::MergedChannels
);
625 connect(process
,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) );
626 connect(process
,SIGNAL(finished(int, QProcess::ExitStatus
)),this, SLOT(slotFinished(int, QProcess::ExitStatus
)) );
627 request
->process
= process
;
629 // process.setEnvironment(envlist);
631 foreach (const QString
&arg
, request
->arg_list
)
634 process
->setProgram(request
->name
,args
);
637 if (!process
->waitForStarted())
639 processRequestReturn(LAUNCHER_ERROR
,"");
643 request
->pid
= process
->pid();
644 QByteArray
data((char *)&request
->pid
, sizeof(int));
645 processRequestReturn(LAUNCHER_OK
,data
);
650 requestList
.append( request
);
651 // Send request to kdeinit.
652 klauncher_header request_header
;
653 QByteArray requestData
;
654 requestData
.reserve(1024);
656 appendLong(requestData
, request
->arg_list
.count() + 1);
657 requestData
.append(request
->name
.toLocal8Bit());
658 requestData
.append('\0');
659 foreach (const QString
&arg
, request
->arg_list
)
660 requestData
.append(arg
.toLocal8Bit()).append('\0');
661 appendLong(requestData
, request
->envs
.count());
662 foreach (const QString
&env
, request
->envs
)
663 requestData
.append(env
.toLocal8Bit()).append('\0');
664 appendLong(requestData
, 0); // avoid_loops, always false here
666 bool startup_notify
= !request
->startup_id
.isNull() && request
->startup_id
!= "0";
668 requestData
.append(request
->startup_id
).append('\0');
670 if (!request
->cwd
.isEmpty())
671 requestData
.append(QFile::encodeName(request
->cwd
)).append('\0');
674 request_header
.cmd
= startup_notify
? LAUNCHER_EXT_EXEC
: LAUNCHER_EXEC_NEW
;
676 request_header
.cmd
= LAUNCHER_EXEC_NEW
;
678 request_header
.arg_length
= requestData
.length();
680 #ifdef KLAUNCHER_VERBOSE_OUTPUT
681 kDebug(7016) << "Asking kdeinit to start" << request
->name
<< request
->arg_list
682 << "cmd=" << commandToString(request_header
.cmd
);
685 write(kdeinitSocket
, &request_header
, sizeof(request_header
));
686 write(kdeinitSocket
, requestData
.data(), requestData
.length());
688 // Wait for pid to return.
689 lastRequest
= request
;
691 slotKDEInitData( kdeinitSocket
);
693 while (lastRequest
!= 0);
697 void KLauncher::exec_blind(const QString
&name
, const QStringList
&arg_list
, const QStringList
&envs
, const QString
&startup_id
)
699 KLaunchRequest
*request
= new KLaunchRequest
;
700 request
->autoStart
= false;
701 request
->name
= name
;
702 request
->arg_list
= arg_list
;
703 request
->dbus_startup_type
= KService::DBusNone
;
705 request
->status
= KLaunchRequest::Launching
;
706 request
->envs
= envs
;
707 // Find service, if any - strip path if needed
708 KService::Ptr service
= KService::serviceByDesktopName( name
.mid( name
.lastIndexOf(QLatin1Char('/')) + 1 ));
710 send_service_startup_info(request
, service
, startup_id
.toLocal8Bit(), QStringList());
711 else // no .desktop file, no startup info
712 cancel_service_startup_info( request
, startup_id
.toLocal8Bit(), envs
);
714 requestStart(request
);
715 // We don't care about this request any longer....
716 requestDone(request
);
722 KLauncher::start_service_by_name(const QString
&serviceName
, const QStringList
&urls
,
723 const QStringList
&envs
, const QString
& startup_id
, bool blind
, const QDBusMessage
&msg
)
725 KService::Ptr service
;
727 service
= KService::serviceByName(serviceName
);
730 requestResult
.result
= ENOENT
;
731 requestResult
.error
= i18n("Could not find service '%1'.", serviceName
);
732 cancel_service_startup_info( NULL
, startup_id
.toLocal8Bit(), envs
); // cancel it if any
735 return start_service(service
, urls
, envs
, startup_id
.toLocal8Bit(), blind
, false, msg
);
739 KLauncher::start_service_by_desktop_path(const QString
&serviceName
, const QStringList
&urls
,
740 const QStringList
&envs
, const QString
& startup_id
, bool blind
, const QDBusMessage
&msg
)
742 KService::Ptr service
;
744 if (QFileInfo(serviceName
).isAbsolute() && QFile::exists(serviceName
))
747 service
= new KService(serviceName
);
751 service
= KService::serviceByDesktopPath(serviceName
);
755 requestResult
.result
= ENOENT
;
756 requestResult
.error
= i18n("Could not find service '%1'.", serviceName
);
757 cancel_service_startup_info( NULL
, startup_id
.toLocal8Bit(), envs
); // cancel it if any
760 return start_service(service
, urls
, envs
, startup_id
.toLocal8Bit(), blind
, false, msg
);
764 KLauncher::start_service_by_desktop_name(const QString
&serviceName
, const QStringList
&urls
,
765 const QStringList
&envs
, const QString
& startup_id
, bool blind
, const QDBusMessage
&msg
)
767 KService::Ptr service
= KService::serviceByDesktopName(serviceName
);
770 requestResult
.result
= ENOENT
;
771 requestResult
.error
= i18n("Could not find service '%1'.", serviceName
);
772 cancel_service_startup_info( NULL
, startup_id
.toLocal8Bit(), envs
); // cancel it if any
775 return start_service(service
, urls
, envs
, startup_id
.toLocal8Bit(), blind
, false, msg
);
779 KLauncher::start_service(KService::Ptr service
, const QStringList
&_urls
,
780 const QStringList
&envs
, const QByteArray
&startup_id
,
781 bool blind
, bool autoStart
, const QDBusMessage
&msg
)
783 QStringList urls
= _urls
;
784 bool runPermitted
= KDesktopFile::isAuthorizedDesktopFile(service
->entryPath());
786 if (!service
->isValid() || !runPermitted
)
788 requestResult
.result
= ENOEXEC
;
789 if (service
->isValid())
790 requestResult
.error
= i18n("Service '%1' must be executable to run.", service
->entryPath());
792 requestResult
.error
= i18n("Service '%1' is malformatted.", service
->entryPath());
793 cancel_service_startup_info( NULL
, startup_id
, envs
); // cancel it if any
796 KLaunchRequest
*request
= new KLaunchRequest
;
797 request
->autoStart
= autoStart
;
799 if ((urls
.count() > 1) && !service
->allowMultipleFiles())
801 // We need to launch the application N times. That sucks.
802 // We ignore the result for application 2 to N.
803 // For the first file we launch the application in the
804 // usual way. The reported result is based on this
806 QStringList::ConstIterator it
= urls
.constBegin();
808 it
!= urls
.constEnd();
811 QStringList singleUrl
;
812 singleUrl
.append(*it
);
813 QByteArray startup_id2
= startup_id
;
814 if( !startup_id2
.isEmpty() && startup_id2
!= "0" )
815 startup_id2
= "0"; // can't use the same startup_id several times
816 start_service( service
, singleUrl
, envs
, startup_id2
, true, false, msg
);
818 QString firstURL
= *(urls
.begin());
820 urls
.append(firstURL
);
822 createArgs(request
, service
, urls
);
824 // We must have one argument at least!
825 if (!request
->arg_list
.count())
827 requestResult
.result
= ENOEXEC
;
828 requestResult
.error
= i18n("Service '%1' is malformatted.", service
->entryPath());
830 cancel_service_startup_info( NULL
, startup_id
, envs
);
834 request
->name
= request
->arg_list
.takeFirst();
836 if (request
->name
.endsWith(QLatin1String("/kioexec"))) {
837 // Special case for kioexec; if createArgs said we were going to use it,
838 // then we have to expect a kioexec-PID, not a org.kde.finalapp...
839 // Testcase: konqueror www.kde.org, RMB on link, open with, kruler.
841 request
->dbus_startup_type
= KService::DBusMulti
;
842 request
->dbus_name
= QString::fromLatin1("org.kde.kioexec");
844 request
->dbus_startup_type
= service
->dbusStartupType();
846 if ((request
->dbus_startup_type
== KService::DBusUnique
) ||
847 (request
->dbus_startup_type
== KService::DBusMulti
)) {
848 const QVariant v
= service
->property(QLatin1String("X-DBUS-ServiceName"));
850 request
->dbus_name
= v
.toString();
852 if (request
->dbus_name
.isEmpty()) {
853 const QString binName
= KRun::binaryName(service
->exec(), true);
854 request
->dbus_name
= QString::fromLatin1("org.kde.") + binName
;
855 request
->tolerant_dbus_name
= QString::fromLatin1("*.") + binName
;
860 #ifdef KLAUNCHER_VERBOSE_OUTPUT
861 kDebug(7016) << "name=" << request
->name
<< "dbus_name=" << request
->dbus_name
862 << "startup type=" << s_DBusStartupTypeToString
[request
->dbus_startup_type
];
866 request
->envs
= envs
;
867 send_service_startup_info( request
, service
, startup_id
, envs
);
869 // Request will be handled later.
870 if (!blind
&& !autoStart
)
872 msg
.setDelayedReply(true);
873 request
->transaction
= msg
;
875 queueRequest(request
);
880 KLauncher::send_service_startup_info( KLaunchRequest
*request
, KService::Ptr service
, const QByteArray
& startup_id
,
881 const QStringList
&envs
)
884 request
->startup_id
= "0";
885 if (startup_id
== "0")
889 if( !KRun::checkStartupNotify( QString(), service
.data(), &silent
, &wmclass
))
892 id
.initId(startup_id
);
894 foreach (const QString
&env
, envs
) {
895 if (env
.startsWith(QLatin1String("DISPLAY=")))
896 dpy_str
= env
.mid(8).toLocal8Bit();
899 if (!dpy_str
.isEmpty() && mCached_dpy
!= NULL
&& dpy_str
!= XDisplayString(mCached_dpy
))
902 dpy
= XOpenDisplay(dpy_str
);
903 request
->startup_id
= id
.id();
905 cancel_service_startup_info( request
, startup_id
, envs
);
909 request
->startup_dpy
= dpy_str
;
911 KStartupInfoData data
;
912 data
.setName( service
->name());
913 data
.setIcon( service
->icon());
914 data
.setDescription( i18n( "Launching %1" , service
->name()));
915 if( !wmclass
.isEmpty())
916 data
.setWMClass( wmclass
);
918 data
.setSilent( KStartupInfoData::Yes
);
919 // the rest will be sent by kdeinit
920 KStartupInfo::sendStartupX( dpy
, id
, data
);
921 if( mCached_dpy
!= dpy
&& mCached_dpy
!= NULL
)
922 XCloseDisplay( mCached_dpy
);
931 KLauncher::cancel_service_startup_info( KLaunchRequest
* request
, const QByteArray
& startup_id
,
932 const QStringList
&envs
)
935 if( request
!= NULL
)
936 request
->startup_id
= "0";
937 if( !startup_id
.isEmpty() && startup_id
!= "0" )
940 foreach (const QString
&env
, envs
) {
941 if (env
.startsWith(QLatin1String("DISPLAY=")))
942 dpy_str
= env
.mid(8);
945 if( !dpy_str
.isEmpty() && mCached_dpy
!= NULL
946 && dpy_str
!= QLatin1String(XDisplayString( mCached_dpy
)) )
949 dpy
= XOpenDisplay( dpy_str
.toLatin1().constData() );
953 id
.initId(startup_id
);
954 KStartupInfo::sendFinishX( dpy
, id
);
955 if( mCached_dpy
!= dpy
&& mCached_dpy
!= NULL
)
956 XCloseDisplay( mCached_dpy
);
963 KLauncher::kdeinit_exec(const QString
&app
, const QStringList
&args
,
964 const QString
& workdir
, const QStringList
&envs
,
965 const QString
&startup_id
, bool wait
, const QDBusMessage
&msg
)
967 KLaunchRequest
*request
= new KLaunchRequest
;
968 request
->autoStart
= false;
969 request
->arg_list
= args
;
972 request
->dbus_startup_type
= KService::DBusWait
;
974 request
->dbus_startup_type
= KService::DBusNone
;
977 request
->startup_id
= startup_id
.toLocal8Bit();
979 request
->envs
= envs
;
980 request
->cwd
= workdir
;
982 if (!app
.endsWith(QLatin1String("kbuildsycoca4"))) { // avoid stupid loop
983 // Find service, if any - strip path if needed
984 const QString desktopName
= app
.mid(app
.lastIndexOf(QLatin1Char('/')) + 1);
985 KService::Ptr service
= KService::serviceByDesktopName(desktopName
);
988 send_service_startup_info(request
, service
,
989 request
->startup_id
, QStringList());
990 else // no .desktop file, no startup info
991 cancel_service_startup_info(request
, request
->startup_id
, envs
);
995 msg
.setDelayedReply(true);
996 request
->transaction
= msg
;
997 queueRequest(request
);
1002 KLauncher::queueRequest(KLaunchRequest
*request
)
1004 requestQueue
.append( request
);
1005 if (!bProcessingQueue
)
1007 bProcessingQueue
= true;
1008 QTimer::singleShot(0, this, SLOT( slotDequeue() ));
1013 KLauncher::slotDequeue()
1016 KLaunchRequest
*request
= requestQueue
.takeFirst();
1018 request
->status
= KLaunchRequest::Launching
;
1019 requestStart(request
);
1020 if (request
->status
!= KLaunchRequest::Launching
)
1023 #ifdef KLAUNCHER_VERBOSE_OUTPUT
1024 kDebug(7016) << "Request handled already";
1026 requestDone( request
);
1029 } while(requestQueue
.count());
1030 bProcessingQueue
= false;
1034 KLauncher::createArgs( KLaunchRequest
*request
, const KService::Ptr service
,
1035 const QStringList
&urls
)
1037 const QStringList params
= KRun::processDesktopExec(*service
, urls
);
1039 for(QStringList::ConstIterator it
= params
.begin();
1040 it
!= params
.end(); ++it
)
1042 request
->arg_list
.append(*it
);
1044 request
->cwd
= service
->path();
1047 ///// IO-Slave functions
1050 KLauncher::requestHoldSlave(const KUrl
&url
, const QString
&app_socket
)
1052 IdleSlave
*slave
= 0;
1053 foreach (IdleSlave
*p
, mSlaveList
)
1063 mSlaveList
.removeAll(slave
);
1064 slave
->connect(app_socket
);
1065 return slave
->pid();
1072 KLauncher::requestSlave(const QString
&protocol
,
1073 const QString
&host
,
1074 const QString
&app_socket
,
1077 IdleSlave
*slave
= 0;
1078 foreach (IdleSlave
*p
, mSlaveList
)
1080 if (p
->match(protocol
, host
, true))
1088 foreach (IdleSlave
*p
, mSlaveList
)
1090 if (p
->match(protocol
, host
, false))
1099 foreach (IdleSlave
*p
, mSlaveList
)
1101 if (p
->match(protocol
, QString(), false))
1110 mSlaveList
.removeAll(slave
);
1111 slave
->connect(app_socket
);
1112 return slave
->pid();
1115 QString name
= KProtocolInfo::exec(protocol
);
1118 error
= i18n("Unknown protocol '%1'.\n", protocol
);
1122 QStringList arg_list
;
1125 arg_list
<< protocol
;
1126 arg_list
<< mConnectionServer
.address();
1127 arg_list
<< app_socket
;
1128 name
= KStandardDirs::findExe(QLatin1String("kioslave"));
1130 QString arg1
= protocol
;
1131 QString arg2
= mConnectionServer
.address();
1132 QString arg3
= app_socket
;
1133 arg_list
.append(arg1
);
1134 arg_list
.append(arg2
);
1135 arg_list
.append(arg3
);
1138 kDebug(7016) << "KLauncher: launching new slave " << name
<< " with protocol=" << protocol
1139 << " args=" << arg_list
<< endl
;
1142 if (mSlaveDebug
== arg1
)
1144 klauncher_header request_header
;
1145 request_header
.cmd
= LAUNCHER_DEBUG_WAIT
;
1146 request_header
.arg_length
= 0;
1147 write(kdeinitSocket
, &request_header
, sizeof(request_header
));
1149 if (mSlaveValgrind
== arg1
)
1151 arg_list
.prepend(KLibLoader::findLibrary(name
));
1152 arg_list
.prepend(KStandardDirs::locate("exe", QString::fromLatin1("kioslave")));
1153 name
= QString::fromLatin1("valgrind");
1154 if (!mSlaveValgrindSkin
.isEmpty()) {
1155 arg_list
.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin
);
1157 arg_list
.prepend(QLatin1String("--tool=memcheck"));
1160 KLaunchRequest
*request
= new KLaunchRequest
;
1161 request
->autoStart
= false;
1162 request
->name
= name
;
1163 request
->arg_list
= arg_list
;
1164 request
->dbus_startup_type
= KService::DBusNone
;
1167 request
->startup_id
= "0";
1169 request
->status
= KLaunchRequest::Launching
;
1170 requestStart(request
);
1171 pid_t pid
= request
->pid
;
1173 // kDebug(7016) << "Slave launched, pid = " << pid;
1175 // We don't care about this request any longer....
1176 requestDone(request
);
1179 error
= i18n("Error loading '%1'.\n", name
);
1185 KLauncher::waitForSlave(int pid
, const QDBusMessage
&msg
)
1187 foreach (IdleSlave
*slave
, mSlaveList
)
1189 if (slave
->pid() == static_cast<pid_t
>(pid
))
1190 return; // Already here.
1192 SlaveWaitRequest
*waitRequest
= new SlaveWaitRequest
;
1193 msg
.setDelayedReply(true);
1194 waitRequest
->transaction
= msg
;
1195 waitRequest
->pid
= static_cast<pid_t
>(pid
);
1196 mSlaveWaitRequest
.append(waitRequest
);
1200 KLauncher::acceptSlave()
1202 IdleSlave
*slave
= new IdleSlave(this);
1203 mConnectionServer
.setNextPendingConnection(&slave
->mConn
);
1204 mSlaveList
.append(slave
);
1205 connect(slave
, SIGNAL(destroyed()), this, SLOT(slotSlaveGone()));
1206 connect(slave
, SIGNAL(statusUpdate(IdleSlave
*)),
1207 this, SLOT(slotSlaveStatus(IdleSlave
*)));
1208 if (!mTimer
.isActive())
1210 mTimer
.start(1000*10);
1215 KLauncher::slotSlaveStatus(IdleSlave
*slave
)
1217 QMutableListIterator
<SlaveWaitRequest
*> it(mSlaveWaitRequest
);
1220 SlaveWaitRequest
*waitRequest
= it
.next();
1221 if (waitRequest
->pid
== slave
->pid())
1223 QDBusConnection::sessionBus().send(waitRequest
->transaction
.createReply());
1231 KLauncher::slotSlaveGone()
1233 IdleSlave
*slave
= (IdleSlave
*) sender();
1234 mSlaveList
.removeAll(slave
);
1235 if ((mSlaveList
.count() == 0) && (mTimer
.isActive()))
1242 KLauncher::idleTimeout()
1244 bool keepOneFileSlave
=true;
1245 time_t now
= time(0);
1246 foreach (IdleSlave
*slave
, mSlaveList
)
1248 if ((slave
->protocol()==QLatin1String("file")) && (keepOneFileSlave
))
1249 keepOneFileSlave
=false;
1250 else if (slave
->age(now
) > SLAVE_MAX_IDLE
)
1252 // killing idle slave
1258 void KLauncher::reparseConfiguration()
1260 KProtocolManager::reparseConfiguration();
1261 foreach (IdleSlave
*slave
, mSlaveList
)
1262 slave
->reparseConfiguration();
1267 KLauncher::slotGotOutput()
1269 KProcess
*p
= static_cast<KProcess
*>(sender());
1270 QByteArray _stdout
= p
->readAllStandardOutput();
1271 kDebug(7016) << _stdout
.data();
1275 KLauncher::slotFinished(int exitCode
, QProcess::ExitStatus exitStatus
)
1277 KProcess
*p
= static_cast<KProcess
*>(sender());
1278 kDebug(7016) << "process finished exitcode=" << exitCode
<< "exitStatus=" << exitStatus
;
1280 foreach (KLaunchRequest
*request
, requestList
)
1282 if (request
->process
== p
)
1284 #ifdef KLAUNCHER_VERBOSE_OUTPUT
1285 kDebug(7016) << "found KProcess, request done";
1287 if (exitCode
== 0 && exitStatus
== QProcess::NormalExit
)
1288 request
->status
= KLaunchRequest::Done
;
1290 request
->status
= KLaunchRequest::Error
;
1291 requestDone(request
);
1292 request
->process
= 0;
1299 #include "klauncher.moc"