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->dead)
00129 return;
00130 if (d->connection->isConnected())
00131 return;
00132 kDebug(7002) << "slave failed to connect to application pid=" << d->m_pid << " protocol=" << d->m_protocol;
00133 if (d->m_pid && (::kill(d->m_pid, 0) == 0))
00134 {
00135 int delta_t = (int) difftime(time(0), d->contact_started);
00136 kDebug(7002) << "slave is slow... pid=" << d->m_pid << " t=" << delta_t;
00137 if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX)
00138 {
00139 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, SLOT(timeout()));
00140 return;
00141 }
00142 }
00143 kDebug(7002) << "Houston, we lost our slave, pid=" << d->m_pid;
00144 d->connection->close();
00145 d->dead = true;
00146 QString arg = d->m_protocol;
00147 if (!d->m_host.isEmpty())
00148 arg += "://"+d->m_host;
00149 kDebug(7002) << "slave died pid = " << d->m_pid;
00150 ref();
00151
00152 emit error(ERR_SLAVE_DIED, arg);
00153
00154 emit slaveDied(this);
00155
00156 deref();
00157 }
00158
00159 Slave::Slave(const QString &protocol, QObject *parent)
00160 : SlaveInterface(*new SlavePrivate(protocol), parent)
00161 {
00162 Q_D(Slave);
00163 d->slaveconnserver->setParent(this);
00164 d->connection = new Connection(this);
00165 connect(d->slaveconnserver, SIGNAL(newConnection()), SLOT(accept()));
00166 }
00167
00168 Slave::~Slave()
00169 {
00170
00171
00172 }
00173
00174 QString Slave::protocol()
00175 {
00176 Q_D(Slave);
00177 return d->m_protocol;
00178 }
00179
00180 void Slave::setProtocol(const QString & protocol)
00181 {
00182 Q_D(Slave);
00183 d->m_protocol = protocol;
00184 }
00185
00186 QString Slave::slaveProtocol()
00187 {
00188 Q_D(Slave);
00189 return d->m_slaveProtocol;
00190 }
00191
00192 QString Slave::host()
00193 {
00194 Q_D(Slave);
00195 return d->m_host;
00196 }
00197
00198 quint16 Slave::port()
00199 {
00200 Q_D(Slave);
00201 return d->m_port;
00202 }
00203
00204 QString Slave::user()
00205 {
00206 Q_D(Slave);
00207 return d->m_user;
00208 }
00209
00210 QString Slave::passwd()
00211 {
00212 Q_D(Slave);
00213 return d->m_passwd;
00214 }
00215
00216 void Slave::setIdle()
00217 {
00218 Q_D(Slave);
00219 d->idle_since = time(0);
00220 }
00221
00222 bool Slave::isConnected()
00223 {
00224 Q_D(Slave);
00225 return d->contacted;
00226 }
00227
00228 void Slave::setConnected(bool c)
00229 {
00230 Q_D(Slave);
00231 d->contacted = c;
00232 }
00233
00234 void Slave::ref()
00235 {
00236 Q_D(Slave);
00237 d->m_refCount++;
00238 }
00239
00240 void Slave::deref()
00241 {
00242 Q_D(Slave);
00243 d->m_refCount--;
00244 if (!d->m_refCount) {
00245 d->connection->disconnect(this);
00246 this->disconnect();
00247 deleteLater();
00248 }
00249 }
00250
00251 time_t Slave::idleTime()
00252 {
00253 Q_D(Slave);
00254 return (time_t) difftime(time(0), d->idle_since);
00255 }
00256
00257 void Slave::setPID(pid_t pid)
00258 {
00259 Q_D(Slave);
00260 d->m_pid = pid;
00261 }
00262
00263 int Slave::slave_pid()
00264 {
00265 Q_D(Slave);
00266 return d->m_pid;
00267 }
00268
00269 bool Slave::isAlive()
00270 {
00271 Q_D(Slave);
00272 return !d->dead;
00273 }
00274
00275 void Slave::hold(const KUrl &url)
00276 {
00277 Q_D(Slave);
00278 ref();
00279 {
00280 QByteArray data;
00281 QDataStream stream( &data, QIODevice::WriteOnly );
00282 stream << url;
00283 d->connection->send( CMD_SLAVE_HOLD, data );
00284 d->connection->close();
00285 d->dead = true;
00286 emit slaveDied(this);
00287 }
00288 deref();
00289
00290 {
00291 KToolInvocation::klauncher()->waitForSlave(d->m_pid);
00292 }
00293 }
00294
00295 void Slave::suspend()
00296 {
00297 Q_D(Slave);
00298 d->connection->suspend();
00299 }
00300
00301 void Slave::resume()
00302 {
00303 Q_D(Slave);
00304 d->connection->resume();
00305 }
00306
00307 bool Slave::suspended()
00308 {
00309 Q_D(Slave);
00310 return d->connection->suspended();
00311 }
00312
00313 void Slave::send(int cmd, const QByteArray &arr)
00314 {
00315 Q_D(Slave);
00316 d->connection->send(cmd, arr);
00317 }
00318
00319 void Slave::gotInput()
00320 {
00321 Q_D(Slave);
00322 ref();
00323 if (!dispatch())
00324 {
00325 d->connection->close();
00326 d->dead = true;
00327 QString arg = d->m_protocol;
00328 if (!d->m_host.isEmpty())
00329 arg += "://"+d->m_host;
00330 kDebug(7002) << "slave died pid = " << d->m_pid;
00331
00332 emit error(ERR_SLAVE_DIED, arg);
00333
00334 emit slaveDied(this);
00335 }
00336 deref();
00337
00338 }
00339
00340 void Slave::kill()
00341 {
00342 Q_D(Slave);
00343 d->dead = true;
00344 kDebug(7002) << "killing slave pid" << d->m_pid
00345 << "(" << QString(d->m_protocol) + "://" + d->m_host << ")";
00346 if (d->m_pid)
00347 {
00348 ::kill(d->m_pid, SIGTERM);
00349 }
00350 }
00351
00352 void Slave::setHost( const QString &host, quint16 port,
00353 const QString &user, const QString &passwd)
00354 {
00355 Q_D(Slave);
00356 d->m_host = host;
00357 d->m_port = port;
00358 d->m_user = user;
00359 d->m_passwd = passwd;
00360
00361 QByteArray data;
00362 QDataStream stream( &data, QIODevice::WriteOnly );
00363 stream << d->m_host << d->m_port << d->m_user << d->m_passwd;
00364 d->connection->send( CMD_HOST, data );
00365 }
00366
00367 void Slave::resetHost()
00368 {
00369 Q_D(Slave);
00370 d->m_host = "<reset>";
00371 }
00372
00373 void Slave::setConfig(const MetaData &config)
00374 {
00375 Q_D(Slave);
00376 QByteArray data;
00377 QDataStream stream( &data, QIODevice::WriteOnly );
00378 stream << config;
00379 d->connection->send( CMD_CONFIG, data );
00380 }
00381
00382 Slave* Slave::createSlave( const QString &protocol, const KUrl& url, int& error, QString& error_text )
00383 {
00384 kDebug(7002) << "createSlave" << protocol << "for" << url;
00385
00386 if (protocol == "data")
00387 return new DataProtocol();
00388 Slave *slave = new Slave(protocol);
00389 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00390
00391 #ifdef Q_OS_UNIX
00392
00393
00394
00395 static bool bForkSlaves = !qgetenv("KDE_FORK_SLAVES").isEmpty();
00396
00397 if (!bForkSlaves)
00398 {
00399
00400 QDBusReply<uint> reply = QDBusConnection::sessionBus().interface()->serviceUid(KToolInvocation::klauncher()->service());
00401 if (reply.isValid() && getuid() != reply)
00402 bForkSlaves = true;
00403 }
00404
00405 if (bForkSlaves)
00406 {
00407 QString _name = KProtocolInfo::exec(protocol);
00408 if (_name.isEmpty())
00409 {
00410 error_text = i18n("Unknown protocol '%1'.", protocol);
00411 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00412 delete slave;
00413 return 0;
00414 }
00415 QString lib_path = KLibLoader::findLibrary(_name);
00416 if (lib_path.isEmpty())
00417 {
00418 error_text = i18n("Can not find io-slave for protocol '%1'.", protocol);
00419 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00420 delete slave;
00421 return 0;
00422 }
00423
00424 QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress;
00425 kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress;
00426
00427 QProcess::startDetached( KStandardDirs::locate("exe", "kioslave"), args );
00428
00429 return slave;
00430 }
00431 #endif
00432
00433 org::kde::KLauncher* klauncher = KToolInvocation::klauncher();
00434 QString errorStr;
00435 QDBusReply<int> reply = klauncher->requestSlave(protocol, url.host(), slaveAddress, errorStr);
00436 if (!reply.isValid()) {
00437 error_text = i18n("Cannot talk to klauncher: %1", klauncher->lastError().message() );
00438 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00439 delete slave;
00440 return 0;
00441 }
00442 pid_t pid = reply;
00443 if (!pid)
00444 {
00445 error_text = i18n("Unable to create io-slave:\nklauncher said: %1", errorStr);
00446 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00447 delete slave;
00448 return 0;
00449 }
00450 slave->setPID(pid);
00451 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00452 return slave;
00453 }
00454
00455 Slave* Slave::holdSlave( const QString &protocol, const KUrl& url )
00456 {
00457
00458
00459 if (protocol == "data")
00460 return 0;
00461 Slave *slave = new Slave(protocol);
00462 QString slaveAddress = slave->d_func()->slaveconnserver->address();
00463 QDBusReply<int> reply = KToolInvocation::klauncher()->requestHoldSlave(url.url(), slaveAddress);
00464 if (!reply.isValid()) {
00465 delete slave;
00466 return 0;
00467 }
00468 pid_t pid = reply;
00469 if (!pid)
00470 {
00471 delete slave;
00472 return 0;
00473 }
00474 slave->setPID(pid);
00475 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00476 return slave;
00477 }
00478
00479 #include "slave.moc"