20#define QT_NO_CAST_FROM_ASCII
36#include <kstartupinfo.h>
40#include <QtCore/QFile>
41#include <qplatformdefs.h>
48#include <kprotocolmanager.h>
56#include <kio/global.h>
57#include <kio/connection.h>
58#include <kio/slaveinterface.h>
61#define SLAVE_MAX_IDLE 30
66 {
"DBusNone",
"DBusUnique",
"DBusMulti",
"DBusWait",
"ERROR" };
73 QObject::connect(&
mConn, SIGNAL(readyRead()),
this, SLOT(
gotInput()));
75 mConn.send( CMD_SLAVE_STATUS );
81template<
int T>
struct PIDType {
typedef pid_t PID_t; } ;
82template<>
struct PIDType<2> {
typedef qint16 PID_t; } ;
83template<>
struct PIDType<4> {
typedef qint32 PID_t; } ;
90 if (
mConn.read( &cmd, data) == -1)
96 else if (cmd == MSG_SLAVE_ACK)
100 else if (cmd != MSG_SLAVE_STATUS)
102 kError(7016) <<
"SlavePool: Unexpected data from slave." << endl;
107 QDataStream stream( data );
108 PIDType<
sizeof(pid_t)>::PID_t stream_pid;
113 stream >> stream_pid >>
protocol >> host >> b;
136 QDataStream stream( &data, QIODevice::WriteOnly);
137 stream << app_socket;
138 mConn.send( CMD_SLAVE_CONNECT, data );
145 mConn.send( CMD_REPARSECONFIGURATION );
154 if (host.isEmpty()) {
164 return (url ==
mUrl);
177#define K_EINTR_LOOP(var, cmd) \
180 } while (var == -1 && errno == EINTR)
187 qWarning() <<
"write failed:" << strerror(errno);
191#ifndef USE_KPROCESS_FOR_KIOSLAVES
194 kdeinitSocket(_kdeinitSocket)
206 mAutoTimer.setSingleShot(
true);
208 QDBusConnection::sessionBus().registerObject(QLatin1String(
"/KLauncher"),
this);
210 connect(&mAutoTimer, SIGNAL(
timeout()),
this, SLOT(slotAutoStart()));
211 connect(QDBusConnection::sessionBus().interface(),
212 SIGNAL(serviceOwnerChanged(QString,QString,QString)),
213 SLOT(slotNameOwnerChanged(QString,QString,QString)));
215 mConnectionServer.listenForRemote();
216 connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave()));
217 if (!mConnectionServer.isListening())
220 qDebug(
"KLauncher: Fatal error, can't create tempfile!");
224 connect(&mTimer, SIGNAL(
timeout()), SLOT(idleTimeout()));
226#ifndef USE_KPROCESS_FOR_KIOSLAVES
227 kdeinitNotifier =
new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read);
228 connect(kdeinitNotifier, SIGNAL(activated(
int)),
229 this, SLOT(slotKDEInitData(
int)));
230 kdeinitNotifier->setEnabled(
true );
233 bProcessingQueue =
false;
235 mSlaveDebug = QString::fromLocal8Bit(qgetenv(
"KDE_SLAVE_DEBUG_WAIT"));
236 if (!mSlaveDebug.isEmpty())
238 qWarning(
"Klauncher running in slave-debug mode for slaves of protocol '%s'", qPrintable(mSlaveDebug));
240 mSlaveValgrind = QString::fromLocal8Bit(qgetenv(
"KDE_SLAVE_VALGRIND"));
241 if (!mSlaveValgrind.isEmpty())
243 mSlaveValgrindSkin = QString::fromLocal8Bit(qgetenv(
"KDE_SLAVE_VALGRIND_SKIN"));
244 qWarning(
"Klauncher running slaves through valgrind for slaves of protocol '%s'", qPrintable(mSlaveValgrind));
246#ifdef USE_KPROCESS_FOR_KIOSLAVES
247 kDebug(7016) <<
"LAUNCHER_OK";
252 kde_safe_write(kdeinitSocket, &request_header,
sizeof(request_header));
284#ifndef USE_KPROCESS_FOR_KIOSLAVES
286 QByteArray requestData;
287 requestData.append(name.toLocal8Bit()).append(
'\0').append(value.toLocal8Bit()).append(
'\0');
289 request_header.
arg_length = requestData.size();
290 kde_safe_write(kdeinitSocket, &request_header,
sizeof(request_header));
298#ifndef USE_KPROCESS_FOR_KIOSLAVES
307 int bytes_left = len;
308 while (bytes_left > 0) {
317 timeval tm = { 30, 0 };
320 select( sock + 1, &in, 0, 0, &tm );
321 if( !FD_ISSET( sock, &in )) {
322 kDebug(7016) <<
"read_socket" << sock <<
"nothing to read, kdeinit4 must be dead";
326 result = read(sock, buffer, bytes_left);
334 else if ((
result == -1) && (errno != EINTR))
344#ifndef USE_KPROCESS_FOR_KIOSLAVES
346 QByteArray requestData;
349 sizeof( request_header));
352 kDebug(7016) <<
"Exiting on read_socket errno:" << errno;
353 KDE_signal( SIGHUP, SIG_IGN);
354 KDE_signal( SIGTERM, SIG_IGN);
357 requestData.resize(request_header.
arg_length);
370 request_data = (
long *) requestData.data();
377 request_data = (
long *) requestData.data();
399 if (!requestData.isEmpty())
405 kWarning(7016)<<
"Unexpected request return" << (
unsigned int) status;
411#ifdef KLAUNCHER_VERBOSE_OUTPUT
412 kDebug(7016) << pid <<
"exitStatus=" << exitStatus;
414 Q_UNUSED(exitStatus);
419#ifdef KLAUNCHER_VERBOSE_OUTPUT
420 kDebug(7016) <<
" had pending request" << request->
pid;
422 if (request->
pid == pid)
427 && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->
dbus_name)) {
429#ifdef KLAUNCHER_VERBOSE_OUTPUT
430 kDebug(7016) << pid <<
"running as a unique app";
434#ifdef KLAUNCHER_VERBOSE_OUTPUT
435 kDebug(7016) << pid <<
"died, requestDone. status=" << request->
status;
442#ifdef KLAUNCHER_VERBOSE_OUTPUT
443 kDebug(7016) <<
"found no pending requests for PID" << pid;
452 const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char(
'-')));
456 if (pendingAppId.startsWith(QLatin1String(
"*."))) {
457 const QString pendingName = pendingAppId.mid(2);
458 const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char(
'.'))+1);
460 return appName == pendingName;
463 return newAppId == pendingAppId;
468 const QString &newOwner)
471 if (appId.isEmpty() || newOwner.isEmpty())
474#ifdef KLAUNCHER_VERBOSE_OUTPUT
475 kDebug(7016) <<
"new app" << appId;
482#ifdef KLAUNCHER_VERBOSE_OUTPUT
488 QDBusConnection::sessionBus().interface()->isServiceRegistered(request->
dbus_name)) {
490#ifdef KLAUNCHER_VERBOSE_OUTPUT
491 kDebug(7016) <<
"OK, unique app" << request->
dbus_name <<
"is running";
496#ifdef KLAUNCHER_VERBOSE_OUTPUT
497 kDebug(7016) <<
"unique app" << request->
dbus_name <<
"not running yet";
503#ifdef KLAUNCHER_VERBOSE_OUTPUT
506 if (rAppId.isEmpty())
510#ifdef KLAUNCHER_VERBOSE_OUTPUT
511 kDebug(7016) <<
"ok, request done";
539 if (service.isEmpty())
562 while (!
start_service(s, QStringList(), QStringList(),
"0",
false,
true, QDBusMessage()));
599 KStartupInfo::sendFinishX( dpy,
id );
613 if (request->
transaction.type() != QDBusMessage::InvalidMessage)
624#ifdef KLAUNCHER_VERBOSE_OUTPUT
625 kDebug(7016) <<
"removing done request" << request->
name <<
"PID" << request->
pid;
634 const int sz = ba.size();
635 ba.resize(sz +
sizeof(
long));
636 memcpy(ba.data() + sz, &l,
sizeof(
long));
642#ifdef USE_KPROCESS_FOR_KIOSLAVES
648 connect(process ,SIGNAL(readyReadStandardOutput()),
this, SLOT(
slotGotOutput()) );
649 connect(process ,SIGNAL(finished(
int,QProcess::ExitStatus)),
this, SLOT(
slotFinished(
int,QProcess::ExitStatus)) );
654 foreach (
const QString &arg, request->
arg_list)
657 QString executable = request->
name;
660 if (!bundlepath.isEmpty())
661 executable = bundlepath;
666 if (!process->waitForStarted())
672 request->
pid = process->
pid();
673 QByteArray data((
char *)&request->
pid,
sizeof(
int));
682 QByteArray requestData;
683 requestData.reserve(1024);
686 requestData.append(request->
name.toLocal8Bit());
687 requestData.append(
'\0');
688 foreach (
const QString &arg, request->
arg_list)
689 requestData.append(arg.toLocal8Bit()).append(
'\0');
691 foreach (
const QString &env, request->
envs)
692 requestData.append(env.toLocal8Bit()).append(
'\0');
697 requestData.append(request->
startup_id).append(
'\0');
699 if (!request->
cwd.isEmpty())
700 requestData.append(QFile::encodeName(request->
cwd)).append(
'\0');
707 request_header.
arg_length = requestData.length();
709#ifdef KLAUNCHER_VERBOSE_OUTPUT
714 kde_safe_write(kdeinitSocket, &request_header,
sizeof(request_header));
715 kde_safe_write(kdeinitSocket, requestData.data(), requestData.length());
726void KLauncher::exec_blind(
const QString &name,
const QStringList &arg_list,
const QStringList &envs,
const QString &startup_id)
730 request->
name = name;
735 request->
envs = envs;
752 const QStringList &envs,
const QString& startup_id,
bool blind,
const QDBusMessage &msg)
756#ifndef KDE_NO_DEPRECATED
766 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind,
false, msg);
771 const QStringList &envs,
const QString& startup_id,
bool blind,
const QDBusMessage &msg)
775 const QFileInfo fi(serviceName);
776 if (fi.isAbsolute() && fi.exists())
779 service =
new KService(serviceName);
795 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind,
false, msg);
800 const QStringList &envs,
const QString& startup_id,
bool blind,
const QDBusMessage &msg)
810 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind,
false, msg);
815 const QStringList &envs,
const QByteArray &startup_id,
816 bool blind,
bool autoStart,
const QDBusMessage &msg)
818 QStringList urls = _urls;
821 if (!service->isValid() || !runPermitted)
824 if (service->isValid())
834 if ((urls.count() > 1) && !service->allowMultipleFiles())
841 QStringList::ConstIterator it = urls.constBegin();
843 it != urls.constEnd();
846 QStringList singleUrl;
847 singleUrl.append(*it);
848 QByteArray startup_id2 = startup_id;
849 if( !startup_id2.isEmpty() && startup_id2 !=
"0" )
851 start_service( service, singleUrl, envs, startup_id2,
true,
false, msg);
853 QString firstURL = *(urls.begin());
855 urls.append(firstURL);
871 if (request->
name.endsWith(QLatin1String(
"/kioexec"))) {
877 request->
dbus_name = QString::fromLatin1(
"org.kde.kioexec");
883 const QVariant v = service->property(QLatin1String(
"X-DBUS-ServiceName"));
888 const QString binName = KRun::binaryName(service->exec(),
true);
889 request->
dbus_name = QString::fromLatin1(
"org.kde.") + binName;
895#ifdef KLAUNCHER_VERBOSE_OUTPUT
901 request->
envs = envs;
907 msg.setDelayedReply(
true);
916 const QStringList &envs )
920 if (startup_id ==
"0")
924 if( !KRun::checkStartupNotify( QString(), service.
data(), &silent, &wmclass ))
927 id.initId(startup_id);
929 foreach (
const QString &env, envs) {
930 if (env.startsWith(QLatin1String(
"DISPLAY=")))
931 dpy_str = env.mid(8).toLocal8Bit();
937 dpy = XOpenDisplay(dpy_str);
946 KStartupInfoData data;
947 data.setName( service->name());
948 data.setIcon( service->icon());
949 data.setDescription(
i18n(
"Launching %1" , service->name()));
950 if( !wmclass.isEmpty())
951 data.setWMClass( wmclass );
953 data.setSilent( KStartupInfoData::Yes );
954 data.setApplicationId( service->entryPath());
956 KStartupInfo::sendStartupX( dpy,
id, data );
968 const QStringList &envs )
971 if( request != NULL )
973 if( !startup_id.isEmpty() && startup_id !=
"0" )
976 foreach (
const QString &env, envs) {
977 if (env.startsWith(QLatin1String(
"DISPLAY=")))
978 dpy_str = env.mid(8);
982 && dpy_str != QLatin1String(XDisplayString(
mCached_dpy )) )
985 dpy = XOpenDisplay( dpy_str.toLatin1().constData() );
989 id.initId(startup_id);
990 KStartupInfo::sendFinishX( dpy,
id );
1000 const QString& workdir,
const QStringList &envs,
1001 const QString &startup_id,
bool wait,
const QDBusMessage &msg)
1006 request->
name = app;
1013 request->
startup_id = startup_id.toLocal8Bit();
1015 request->
envs = envs;
1016 request->
cwd = workdir;
1018 if (!app.endsWith(QLatin1String(
"kbuildsycoca4"))) {
1020 const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char(
'/')) + 1);
1029 msg.setDelayedReply(
true);
1057#ifdef KLAUNCHER_VERBOSE_OUTPUT
1058 kDebug(7016) <<
"Request handled already";
1069 const QStringList &urls)
1071 const QStringList params = KRun::processDesktopExec(*service, urls);
1073 for(QStringList::ConstIterator it = params.begin();
1074 it != params.end(); ++it)
1079 const QString& path = service->path();
1080 if (!path.isEmpty()) {
1081 request->
cwd = path;
1082 }
else if (!urls.isEmpty()) {
1083 const KUrl url(urls.first());
1108 return slave->
pid();
1115 const QString &host,
1116 const QString &app_socket,
1122 if (p->
match(protocol, host,
true))
1132 if (p->
match(protocol, host,
false))
1143 if (p->
match(protocol, QString(),
false))
1154 return slave->
pid();
1157 QString name = KProtocolInfo::exec(protocol);
1160 error =
i18n(
"Unknown protocol '%1'.\n", protocol);
1164 QStringList arg_list;
1165#ifdef USE_KPROCESS_FOR_KIOSLAVES
1167 arg_list << protocol;
1169 arg_list << app_socket;
1172 QString arg1 = protocol;
1174 QString arg3 = app_socket;
1175 arg_list.append(arg1);
1176 arg_list.append(arg2);
1177 arg_list.append(arg3);
1180 kDebug(7016) <<
"KLauncher: launching new slave " << name <<
" with protocol=" << protocol
1181 <<
" args=" << arg_list << endl;
1186#ifndef USE_KPROCESS_FOR_KIOSLAVES
1190 kde_safe_write(kdeinitSocket, &request_header,
sizeof(request_header));
1192 name = QString::fromLatin1(
"gdb");
1196#ifndef USE_KPROCESS_FOR_KIOSLAVES
1201 name = QString::fromLatin1(
"valgrind");
1206 arg_list.prepend(QLatin1String(
"--tool=memcheck"));
1211 request->
name = name;
1220 pid_t pid = request->
pid;
1228 error =
i18n(
"Error loading '%1'.\n", name);
1248 if (slave->
pid() ==
static_cast<pid_t
>(pid))
1252 msg.setDelayedReply(
true);
1254 waitRequest->
pid =
static_cast<pid_t
>(pid);
1264 connect(slave, SIGNAL(destroyed()),
this, SLOT(
slotSlaveGone()));
1265 connect(slave, SIGNAL(statusUpdate(
IdleSlave*)),
1280 if (waitRequest->
pid == slave->
pid())
1282 QDBusConnection::sessionBus().send(waitRequest->
transaction.createReply());
1303 bool keepOneFileSlave=
true;
1304 time_t now = time(0);
1307 if ((slave->
protocol()==QLatin1String(
"file")) && (keepOneFileSlave))
1308 keepOneFileSlave=
false;
1319 KProtocolManager::reparseConfiguration();
1328#ifdef USE_KPROCESS_FOR_KIOSLAVES
1330 QByteArray _stdout = p->readAllStandardOutput();
1331 kDebug(7016) << _stdout.data();
1338#ifdef USE_KPROCESS_FOR_KIOSLAVES
1340 kDebug(7016) <<
"process finished exitcode=" << exitCode <<
"exitStatus=" << exitStatus;
1346#ifdef KLAUNCHER_VERBOSE_OUTPUT
1347 kDebug(7016) <<
"found KProcess, request done";
1349 if (exitCode == 0 && exitStatus == QProcess::NormalExit)
1360 Q_UNUSED(exitStatus);
1368#ifndef USE_KPROCESS_FOR_KIOSLAVES
1372 kde_safe_write(kdeinitSocket, &request_header,
sizeof(request_header));
1376#include "klauncher.moc"
void statusUpdate(IdleSlave *)
void reparseConfiguration()
void connect(const QString &app_socket)
bool onHold(const KUrl &url) const
IdleSlave(QObject *parent)
int age(time_t now) const
bool match(const QString &protocol, const QString &host, bool connected) const
static bool isAuthorizedDesktopFile(const QString &path)
KService::DBusStartupType dbus_startup_type
QString tolerant_dbus_name
void createArgs(KLaunchRequest *request, const KService::Ptr service, const QStringList &url)
void requestDone(KLaunchRequest *request)
QList< SlaveWaitRequest * > mSlaveWaitRequest
QString mSlaveValgrindSkin
QList< IdleSlave * > mSlaveList
void autoStart(int phase=1)
QList< KLaunchRequest * > requestQueue
bool start_service(KService::Ptr service, const QStringList &urls, const QStringList &envs, const QByteArray &startup_id, bool blind, bool autoStart, const QDBusMessage &msg)
void cancel_service_startup_info(KLaunchRequest *request, const QByteArray &startup_id, const QStringList &envs)
void processDied(pid_t pid, long exitStatus)
void slotKDEInitData(int)
KIO::ConnectionServer mConnectionServer
void slotSlaveStatus(IdleSlave *)
QList< KLaunchRequest * > requestList
KLaunchRequest * lastRequest
void queueRequest(KLaunchRequest *)
serviceResult requestResult
void processRequestReturn(int status, const QByteArray &requestData)
void send_service_startup_info(KLaunchRequest *request, KService::Ptr service, const QByteArray &startup_id, const QStringList &envs)
void slotFinished(int exitCode, QProcess::ExitStatus exitStatus)
void requestStart(KLaunchRequest *request)
void slotNameOwnerChanged(const QString &name, const QString &oldOnwer, const QString &newOwner)
void setOutputChannelMode(OutputChannelMode mode)
void setProgram(const QString &exe, const QStringList &args=QStringList())
static Ptr serviceByName(const QString &_name)
static Ptr serviceByDesktopName(const QString &_name)
static Ptr serviceByDesktopPath(const QString &_path)
static QString findExe(const QString &appname, const QString &pathstr=QString(), SearchOptions options=NoSearchOptions)
static QString locate(const char *type, const QString &filename, const KComponentData &cData=KGlobal::mainComponent())
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
QDBusReply< void > setLaunchEnv(const QString &name, const QString &value)
QDBusReply< int > kdeinit_exec(const QString &app, const QStringList &args, const QStringList &env, const QString &startup_id, QString &dbusServiceName, QString &error, int &pid)
QDBusReply< int > start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, QString &dbusServiceName, QString &error, int &pid)
QDBusReply< bool > checkForHeldSlave(const QString &url)
QDBusReply< void > autoStart()
QDBusReply< int > start_service_by_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, QString &dbusServiceName, QString &error, int &pid)
QDBusReply< int > requestSlave(const QString &protocol, const QString &host, const QString &app_socket, QString &error)
QDBusReply< void > waitForSlave(int pid)
QDBusReply< void > reparseConfiguration()
QDBusReply< int > requestHoldSlave(const QString &url, const QString &app_socket)
QDBusReply< void > exec_blind(const QString &name, const QStringList &arg_list)
QDBusReply< int > start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, const QStringList &envs, const QString &startup_id, bool blind, QString &dbusServiceName, QString &error, int &pid)
static int read_socket(int sock, char *buffer, int len)
static KLauncher * g_klauncher_self
ssize_t kde_safe_write(int fd, const void *buf, size_t count)
#define K_EINTR_LOOP(var, cmd)
static const char *const s_DBusStartupTypeToString[]
static void appendLong(QByteArray &ba, long l)
static bool matchesPendingRequest(const QString &appId, const QString &pendingAppId)
const char * commandToString(int command)
#define LAUNCHER_CHILD_DIED
#define LAUNCHER_DEBUG_WAIT
#define LAUNCHER_TERMINATE_KDEINIT
#define LAUNCHER_EXEC_NEW
#define LAUNCHER_EXT_EXEC
QString i18n(const char *text)
const KComponentData & mainComponent()