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 d->connection->disconnect(this);
00245 this->disconnect();
00246 deleteLater();
00247 }
00248 }
00249
00250 time_t Slave::idleTime()
00251 {
00252 Q_D(Slave);
00253 return (time_t) difftime(time(0), d->idle_since);
00254 }
00255
00256 void Slave::setPID(pid_t pid)
00257 {
00258 Q_D(Slave);
00259 d->m_pid = pid;
00260 }
00261
00262 int Slave::slave_pid()
00263 {
00264 Q_D(Slave);
00265 return d->m_pid;
00266 }
00267
00268 bool Slave::isAlive()
00269 {
00270 Q_D(Slave);
00271 return !d->dead;
00272 }
00273
00274 void Slave::hold(const KUrl &url)
00275 {
00276 Q_D(Slave);
00277 ref();
00278 {
00279 QByteArray data;
00280 QDataStream stream( &data, QIODevice::WriteOnly );
00281 stream << url;
00282 d->connection->send( CMD_SLAVE_HOLD, data );
00283 d->connection->close();
00284 d->dead = true;
00285 emit slaveDied(this);
00286 }
00287 deref();
00288
00289 {
00290 KToolInvocation::klauncher()->waitForSlave(d->m_pid);
00291 }
00292 }
00293
00294 void Slave::suspend()
00295 {
00296 Q_D(Slave);
00297 d->connection->suspend();
00298 }
00299
00300 void Slave::resume()
00301 {
00302 Q_D(Slave);
00303 d->connection->resume();
00304 }
00305
00306 bool Slave::suspended()
00307 {
00308 Q_D(Slave);
00309 return d->connection->suspended();
00310 }
00311
00312 void Slave::send(int cmd, const QByteArray &arr)
00313 {
00314 Q_D(Slave);
00315 d->connection->send(cmd, arr);
00316 }
00317
00318 void Slave::gotInput()
00319 {
00320 Q_D(Slave);
00321 ref();
00322 if (!dispatch())
00323 {
00324 d->connection->close();
00325 d->dead = true;
00326 QString arg = d->m_protocol;
00327 if (!d->m_host.isEmpty())
00328 arg += "://"+d->m_host;
00329 kDebug(7002) << "slave died pid = " << d->m_pid;
00330
00331 emit error(ERR_SLAVE_DIED, arg);
00332
00333 emit slaveDied(this);
00334 }
00335 deref();
00336
00337 }
00338
00339 void Slave::kill()
00340 {
00341 Q_D(Slave);
00342 d->dead = true;
00343 kDebug(7002) << "killing slave pid" << d->m_pid
00344 << "(" << QString(d->m_protocol) + "://" + d->m_host << ")";
00345 if (d->m_pid)
00346 {
00347 ::kill(d->m_pid, SIGTERM);
00348 }
00349 }
00350
00351 void Slave::setHost( const QString &host, quint16 port,
00352 const QString &user, const QString &passwd)
00353 {
00354 Q_D(Slave);
00355 d->m_host = host;
00356 d->m_port = port;
00357 d->m_user = user;
00358 d->m_passwd = passwd;
00359
00360 QByteArray data;
00361 QDataStream stream( &data, QIODevice::WriteOnly );
00362 stream << d->m_host << d->m_port << d->m_user << d->m_passwd;
00363 d->connection->send( CMD_HOST, data );
00364 }
00365
00366 void Slave::resetHost()
00367 {
00368 Q_D(Slave);
00369 d->m_host = "<reset>";
00370 }
00371
00372 void Slave::setConfig(const MetaData &config)
00373 {
00374 Q_D(Slave);
00375 QByteArray data;
00376 QDataStream stream( &data, QIODevice::WriteOnly );
00377 stream << config;
00378 d->connection->send( CMD_CONFIG, data );
00379 }
00380
00381 Slave* Slave::createSlave( const QString &protocol, const KUrl& url, int& error, QString& error_text )
00382 {
00383 kDebug(7002) << "createSlave" << protocol << "for" << url;
00384
00385 if (protocol == "data")
00386 return new DataProtocol();
00387 Slave *slave = new Slave(protocol);
00388 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00389
00390 #ifdef Q_OS_UNIX
00391
00392
00393
00394 static bool bForkSlaves = !qgetenv("KDE_FORK_SLAVES").isEmpty();
00395
00396 if (!bForkSlaves)
00397 {
00398
00399 QDBusReply<uint> reply = QDBusConnection::sessionBus().interface()->serviceUid(KToolInvocation::klauncher()->service());
00400 if (reply.isValid() && getuid() != reply)
00401 bForkSlaves = true;
00402 }
00403
00404 if (bForkSlaves)
00405 {
00406 QString _name = KProtocolInfo::exec(protocol);
00407 if (_name.isEmpty())
00408 {
00409 error_text = i18n("Unknown protocol '%1'.", protocol);
00410 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00411 delete slave;
00412 return 0;
00413 }
00414 QString lib_path = KLibLoader::findLibrary(QFile::encodeName(_name));
00415 if (lib_path.isEmpty())
00416 {
00417 error_text = i18n("Can not find io-slave for protocol '%1'.", protocol);
00418 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00419 delete slave;
00420 return 0;
00421 }
00422
00423 QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress;
00424 kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress;
00425
00426 QProcess::startDetached( KStandardDirs::locate("exe", "kioslave"), args );
00427
00428 return slave;
00429 }
00430 #endif
00431
00432 org::kde::KLauncher* klauncher = KToolInvocation::klauncher();
00433 QString errorStr;
00434 QDBusReply<int> reply = klauncher->requestSlave(protocol, url.host(), slaveAddress, errorStr);
00435 if (!reply.isValid()) {
00436 error_text = i18n("Cannot talk to klauncher: %1", klauncher->lastError().message() );
00437 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00438 delete slave;
00439 return 0;
00440 }
00441 pid_t pid = reply;
00442 if (!pid)
00443 {
00444 error_text = i18n("Unable to create io-slave:\nklauncher said: %1", errorStr);
00445 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00446 delete slave;
00447 return 0;
00448 }
00449 slave->setPID(pid);
00450 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00451 return slave;
00452 }
00453
00454 Slave* Slave::holdSlave( const QString &protocol, const KUrl& url )
00455 {
00456
00457
00458 if (protocol == "data")
00459 return 0;
00460 Slave *slave = new Slave(protocol);
00461 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00462 QDBusReply<int> reply = KToolInvocation::klauncher()->requestHoldSlave(url.url(), slaveAddress);
00463 if (!reply.isValid()) {
00464 delete slave;
00465 return 0;
00466 }
00467 pid_t pid = reply;
00468 if (!pid)
00469 {
00470 delete slave;
00471 return 0;
00472 }
00473 slave->setPID(pid);
00474 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00475 return slave;
00476 }
00477
00478 #include "slave.moc"