00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "slave.h"
00022
00023 #include <config.h>
00024
00025 #include <time.h>
00026 #include <errno.h>
00027 #include <unistd.h>
00028 #include <stdlib.h>
00029 #include <stdio.h>
00030 #include <signal.h>
00031 #include <sys/types.h>
00032
00033 #include <QtCore/QBool>
00034 #include <QtCore/QFile>
00035 #include <QtCore/QTimer>
00036 #include <QtDBus/QtDBus>
00037 #include <QtCore/QProcess>
00038
00039 #include <kdebug.h>
00040 #include <klocale.h>
00041 #include <kglobal.h>
00042 #include <kstandarddirs.h>
00043 #include <kapplication.h>
00044 #include <ktemporaryfile.h>
00045 #include <ktoolinvocation.h>
00046 #include <klauncher_iface.h>
00047
00048 #include "dataprotocol.h"
00049 #include "kservice.h"
00050 #include <kio/global.h>
00051 #include "kio/connection.h"
00052 #include <kprotocolmanager.h>
00053 #include <kprotocolinfo.h>
00054
00055 #include "slaveinterface_p.h"
00056
00057 using namespace KIO;
00058
00059 #define SLAVE_CONNECTION_TIMEOUT_MIN 2
00060
00061
00062
00063
00064
00065 #ifdef NDEBUG
00066 #define SLAVE_CONNECTION_TIMEOUT_MAX 10
00067 #else
00068 #define SLAVE_CONNECTION_TIMEOUT_MAX 3600
00069 #endif
00070
00071 namespace KIO {
00072
00076 class SlavePrivate: public SlaveInterfacePrivate
00077 {
00078 public:
00079 SlavePrivate(const QString &protocol) :
00080 m_protocol(protocol),
00081 m_slaveProtocol(protocol),
00082 slaveconnserver(new KIO::ConnectionServer),
00083 m_pid(0),
00084 m_port(0),
00085 contacted(false),
00086 dead(false),
00087 contact_started(time(0)),
00088 m_refCount(1)
00089 {
00090 slaveconnserver->listenForRemote();
00091 if ( !slaveconnserver->isListening() )
00092 kWarning() << "Connection server not listening, could not connect";
00093 }
00094 ~SlavePrivate()
00095 {
00096 delete slaveconnserver;
00097 }
00098
00099 QString m_protocol;
00100 QString m_slaveProtocol;
00101 QString m_host;
00102 QString m_user;
00103 QString m_passwd;
00104 KIO::ConnectionServer *slaveconnserver;
00105 pid_t m_pid;
00106 quint16 m_port;
00107 bool contacted;
00108 bool dead;
00109 time_t contact_started;
00110 time_t idle_since;
00111 int m_refCount;
00112 };
00113 }
00114
00115 void Slave::accept()
00116 {
00117 Q_D(Slave);
00118 d->slaveconnserver->setNextPendingConnection(d->connection);
00119 d->slaveconnserver->deleteLater();
00120 d->slaveconnserver = 0;
00121
00122 connect(d->connection, SIGNAL(readyRead()), SLOT(gotInput()));
00123 }
00124
00125 void Slave::timeout()
00126 {
00127 Q_D(Slave);
00128 if (d->connection->isConnected())
00129 return;
00130
00131 kDebug(7002) << "slave failed to connect to application pid=" << d->m_pid << " protocol=" << d->m_protocol;
00132 if (d->m_pid && (::kill(d->m_pid, 0) == 0))
00133 {
00134 int delta_t = (int) difftime(time(0), d->contact_started);
00135 kDebug(7002) << "slave is slow... pid=" << d->m_pid << " t=" << delta_t;
00136 if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX)
00137 {
00138 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, SLOT(timeout()));
00139 return;
00140 }
00141 }
00142 kDebug(7002) << "Houston, we lost our slave, pid=" << d->m_pid;
00143 d->connection->close();
00144 d->dead = true;
00145 QString arg = d->m_protocol;
00146 if (!d->m_host.isEmpty())
00147 arg += "://"+d->m_host;
00148 kDebug(7002) << "slave died pid = " << d->m_pid;
00149 ref();
00150
00151 emit error(ERR_SLAVE_DIED, arg);
00152
00153 emit slaveDied(this);
00154
00155 deref();
00156 }
00157
00158 Slave::Slave(const QString &protocol, QObject *parent)
00159 : SlaveInterface(*new SlavePrivate(protocol), parent)
00160 {
00161 Q_D(Slave);
00162 d->slaveconnserver->setParent(this);
00163 d->connection = new Connection(this);
00164 connect(d->slaveconnserver, SIGNAL(newConnection()), SLOT(accept()));
00165 }
00166
00167 Slave::~Slave()
00168 {
00169
00170
00171 }
00172
00173 QString Slave::protocol()
00174 {
00175 Q_D(Slave);
00176 return d->m_protocol;
00177 }
00178
00179 void Slave::setProtocol(const QString & protocol)
00180 {
00181 Q_D(Slave);
00182 d->m_protocol = protocol;
00183 }
00184
00185 QString Slave::slaveProtocol()
00186 {
00187 Q_D(Slave);
00188 return d->m_slaveProtocol;
00189 }
00190
00191 QString Slave::host()
00192 {
00193 Q_D(Slave);
00194 return d->m_host;
00195 }
00196
00197 quint16 Slave::port()
00198 {
00199 Q_D(Slave);
00200 return d->m_port;
00201 }
00202
00203 QString Slave::user()
00204 {
00205 Q_D(Slave);
00206 return d->m_user;
00207 }
00208
00209 QString Slave::passwd()
00210 {
00211 Q_D(Slave);
00212 return d->m_passwd;
00213 }
00214
00215 void Slave::setIdle()
00216 {
00217 Q_D(Slave);
00218 d->idle_since = time(0);
00219 }
00220
00221 bool Slave::isConnected()
00222 {
00223 Q_D(Slave);
00224 return d->contacted;
00225 }
00226
00227 void Slave::setConnected(bool c)
00228 {
00229 Q_D(Slave);
00230 d->contacted = c;
00231 }
00232
00233 void Slave::ref()
00234 {
00235 Q_D(Slave);
00236 d->m_refCount++;
00237 }
00238
00239 void Slave::deref()
00240 {
00241 Q_D(Slave);
00242 d->m_refCount--;
00243 if (!d->m_refCount)
00244 deleteLater();
00245 }
00246
00247 time_t Slave::idleTime()
00248 {
00249 Q_D(Slave);
00250 return (time_t) difftime(time(0), d->idle_since);
00251 }
00252
00253 void Slave::setPID(pid_t pid)
00254 {
00255 Q_D(Slave);
00256 d->m_pid = pid;
00257 }
00258
00259 int Slave::slave_pid()
00260 {
00261 Q_D(Slave);
00262 return d->m_pid;
00263 }
00264
00265 bool Slave::isAlive()
00266 {
00267 Q_D(Slave);
00268 return !d->dead;
00269 }
00270
00271 void Slave::hold(const KUrl &url)
00272 {
00273 Q_D(Slave);
00274 ref();
00275 {
00276 QByteArray data;
00277 QDataStream stream( &data, QIODevice::WriteOnly );
00278 stream << url;
00279 d->connection->send( CMD_SLAVE_HOLD, data );
00280 d->connection->close();
00281 d->dead = true;
00282 emit slaveDied(this);
00283 }
00284 deref();
00285
00286 {
00287 KToolInvocation::klauncher()->waitForSlave(d->m_pid);
00288 }
00289 }
00290
00291 void Slave::suspend()
00292 {
00293 Q_D(Slave);
00294 d->connection->suspend();
00295 }
00296
00297 void Slave::resume()
00298 {
00299 Q_D(Slave);
00300 d->connection->resume();
00301 }
00302
00303 bool Slave::suspended()
00304 {
00305 Q_D(Slave);
00306 return d->connection->suspended();
00307 }
00308
00309 void Slave::send(int cmd, const QByteArray &arr)
00310 {
00311 Q_D(Slave);
00312 d->connection->send(cmd, arr);
00313 }
00314
00315 void Slave::gotInput()
00316 {
00317 Q_D(Slave);
00318 ref();
00319 if (!dispatch())
00320 {
00321 d->connection->close();
00322 d->dead = true;
00323 QString arg = d->m_protocol;
00324 if (!d->m_host.isEmpty())
00325 arg += "://"+d->m_host;
00326 kDebug(7002) << "slave died pid = " << d->m_pid;
00327
00328 emit error(ERR_SLAVE_DIED, arg);
00329
00330 emit slaveDied(this);
00331 }
00332 deref();
00333
00334 }
00335
00336 void Slave::kill()
00337 {
00338 Q_D(Slave);
00339 d->dead = true;
00340 kDebug(7002) << "killing slave pid=" << d->m_pid << " (" << d->m_protocol << "://"
00341 << d->m_host << ")";
00342 if (d->m_pid)
00343 {
00344 ::kill(d->m_pid, SIGTERM);
00345 }
00346 }
00347
00348 void Slave::setHost( const QString &host, quint16 port,
00349 const QString &user, const QString &passwd)
00350 {
00351 Q_D(Slave);
00352 d->m_host = host;
00353 d->m_port = port;
00354 d->m_user = user;
00355 d->m_passwd = passwd;
00356
00357 QByteArray data;
00358 QDataStream stream( &data, QIODevice::WriteOnly );
00359 stream << d->m_host << d->m_port << d->m_user << d->m_passwd;
00360 d->connection->send( CMD_HOST, data );
00361 }
00362
00363 void Slave::resetHost()
00364 {
00365 Q_D(Slave);
00366 d->m_host = "<reset>";
00367 }
00368
00369 void Slave::setConfig(const MetaData &config)
00370 {
00371 Q_D(Slave);
00372 QByteArray data;
00373 QDataStream stream( &data, QIODevice::WriteOnly );
00374 stream << config;
00375 d->connection->send( CMD_CONFIG, data );
00376 }
00377
00378 Slave* Slave::createSlave( const QString &protocol, const KUrl& url, int& error, QString& error_text )
00379 {
00380 kDebug(7002) << "createSlave" << protocol << "for" << url;
00381
00382 if (protocol == "data")
00383 return new DataProtocol();
00384 Slave *slave = new Slave(protocol);
00385 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00386
00387 #ifdef Q_OS_UNIX
00388
00389
00390
00391 static bool bForkSlaves = !qgetenv("KDE_FORK_SLAVES").isEmpty();
00392
00393 if (!bForkSlaves)
00394 {
00395
00396 QDBusReply<uint> reply = QDBusConnection::sessionBus().interface()->serviceUid(KToolInvocation::klauncher()->service());
00397 if (reply.isValid() && getuid() != reply)
00398 bForkSlaves = true;
00399 }
00400
00401 if (bForkSlaves)
00402 {
00403 QString _name = KProtocolInfo::exec(protocol);
00404 if (_name.isEmpty())
00405 {
00406 error_text = i18n("Unknown protocol '%1'.", protocol);
00407 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00408 delete slave;
00409 return 0;
00410 }
00411 QString lib_path = KLibLoader::findLibrary(QFile::encodeName(_name));
00412 if (lib_path.isEmpty())
00413 {
00414 error_text = i18n("Can not find io-slave for protocol '%1'.", protocol);
00415 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00416 return 0;
00417 }
00418
00419 QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress;
00420 kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress;
00421
00422 QProcess::startDetached( KStandardDirs::locate("exe", "kioslave"), args );
00423
00424 return slave;
00425 }
00426 #endif
00427
00428 org::kde::KLauncher* klauncher = KToolInvocation::klauncher();
00429 QString errorStr;
00430 QDBusReply<int> reply = klauncher->requestSlave(protocol, url.host(), slaveAddress, errorStr);
00431 if (!reply.isValid()) {
00432 error_text = i18n("Cannot talk to klauncher: %1", klauncher->lastError().message() );
00433 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00434 delete slave;
00435 return 0;
00436 }
00437 pid_t pid = reply;
00438 if (!pid)
00439 {
00440 error_text = i18n("Unable to create io-slave:\nklauncher said: %1", errorStr);
00441 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00442 delete slave;
00443 return 0;
00444 }
00445 slave->setPID(pid);
00446 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00447 return slave;
00448 }
00449
00450 Slave* Slave::holdSlave( const QString &protocol, const KUrl& url )
00451 {
00452
00453
00454 if (protocol == "data")
00455 return 0;
00456 Slave *slave = new Slave(protocol);
00457 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00458 QDBusReply<int> reply = KToolInvocation::klauncher()->requestHoldSlave(url.url(), slaveAddress);
00459 if (!reply.isValid()) {
00460 delete slave;
00461 return 0;
00462 }
00463 pid_t pid = reply;
00464 if (!pid)
00465 {
00466 delete slave;
00467 return 0;
00468 }
00469 slave->setPID(pid);
00470 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00471 return slave;
00472 }
00473
00474 #include "slave.moc"