00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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 <qfile.h>
00034 #include <qtimer.h>
00035
00036 #include <dcopclient.h>
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039 #include <kglobal.h>
00040 #include <kstandarddirs.h>
00041 #include <kapplication.h>
00042 #include <ktempfile.h>
00043 #include <ksock.h>
00044 #include <kprocess.h>
00045 #include <klibloader.h>
00046
00047 #include "kio/dataprotocol.h"
00048 #include "kio/slave.h"
00049 #include "kio/kservice.h"
00050 #include <kio/global.h>
00051 #include <kprotocolmanager.h>
00052 #include <kprotocolinfo.h>
00053
00054 #ifdef HAVE_PATHS_H
00055 #include <paths.h>
00056 #endif
00057
00058 #ifndef _PATH_TMP
00059 #define _PATH_TMP "/tmp"
00060 #endif
00061
00062 using namespace KIO;
00063
00064 #define SLAVE_CONNECTION_TIMEOUT_MIN 2
00065
00066
00067
00068
00069
00070 #ifdef NDEBUG
00071 #define SLAVE_CONNECTION_TIMEOUT_MAX 10
00072 #else
00073 #define SLAVE_CONNECTION_TIMEOUT_MAX 3600
00074 #endif
00075
00076 namespace KIO {
00077
00081 class SlavePrivate {
00082 public:
00083 bool derived;
00084
00085
00086 SlavePrivate(bool derived) : derived(derived) {}
00087 };
00088 }
00089
00090 void Slave::accept(KSocket *socket)
00091 {
00092 slaveconn.init(socket);
00093 delete serv;
00094 serv = 0;
00095 slaveconn.connect(this, SLOT(gotInput()));
00096 unlinkSocket();
00097 }
00098
00099 void Slave::unlinkSocket()
00100 {
00101 if (m_socket.isEmpty()) return;
00102 QCString filename = QFile::encodeName(m_socket);
00103 unlink(filename.data());
00104 m_socket = QString::null;
00105 }
00106
00107 void Slave::timeout()
00108 {
00109 if (!serv) return;
00110 kdDebug(7002) << "slave failed to connect to application pid=" << m_pid << " protocol=" << m_protocol << endl;
00111 if (m_pid && (::kill(m_pid, 0) == 0))
00112 {
00113 int delta_t = (int) difftime(time(0), contact_started);
00114 kdDebug(7002) << "slave is slow... pid=" << m_pid << " t=" << delta_t << endl;
00115 if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX)
00116 {
00117 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, SLOT(timeout()));
00118 return;
00119 }
00120 }
00121 kdDebug(7002) << "Houston, we lost our slave, pid=" << m_pid << endl;
00122 delete serv;
00123 serv = 0;
00124 unlinkSocket();
00125 dead = true;
00126 QString arg = m_protocol;
00127 if (!m_host.isEmpty())
00128 arg += "://"+m_host;
00129 kdDebug(7002) << "slave died pid = " << m_pid << endl;
00130 ref();
00131
00132 emit error(ERR_SLAVE_DIED, arg);
00133
00134 emit slaveDied(this);
00135
00136 deref();
00137 }
00138
00139 Slave::Slave(KServerSocket *socket, const QString &protocol, const QString &socketname)
00140 : SlaveInterface(&slaveconn), serv(socket), contacted(false),
00141 d(new SlavePrivate(false))
00142 {
00143 m_refCount = 1;
00144 m_protocol = protocol;
00145 m_slaveProtocol = protocol;
00146 m_socket = socketname;
00147 dead = false;
00148 contact_started = time(0);
00149 idle_since = contact_started;
00150 m_pid = 0;
00151 m_port = 0;
00152 connect(serv, SIGNAL(accepted( KSocket* )),
00153 SLOT(accept(KSocket*) ) );
00154 }
00155
00156 Slave::Slave(bool , KServerSocket *socket, const QString &protocol,
00157 const QString &socketname)
00158 : SlaveInterface(&slaveconn), serv(socket), contacted(false),
00159 d(new SlavePrivate(true))
00160 {
00161
00162 m_refCount = 1;
00163 m_protocol = protocol;
00164 m_slaveProtocol = protocol;
00165 m_socket = socketname;
00166 dead = false;
00167 contact_started = time(0);
00168 idle_since = contact_started;
00169 m_pid = 0;
00170 m_port = 0;
00171 if (serv != 0) {
00172 connect(serv, SIGNAL(accepted( KSocket* )),
00173 SLOT(accept(KSocket*) ) );
00174 }
00175 }
00176
00177 Slave::~Slave()
00178 {
00179
00180 if (serv != 0) {
00181 delete serv;
00182 serv = 0;
00183 }
00184 unlinkSocket();
00185 m_pid = 99999;
00186 delete d;
00187 d = 0;
00188 }
00189
00190 void Slave::setProtocol(const QString & protocol)
00191 {
00192 m_protocol = protocol;
00193 }
00194
00195 void Slave::setIdle()
00196 {
00197 idle_since = time(0);
00198 }
00199
00200 time_t Slave::idleTime()
00201 {
00202 return (time_t) difftime(time(0), idle_since);
00203 }
00204
00205 void Slave::setPID(pid_t pid)
00206 {
00207 m_pid = pid;
00208 }
00209
00210 void Slave::hold(const KURL &url)
00211 {
00212 if (d->derived) {
00213 HoldParams params;
00214 params.url = &url;
00215 virtual_hook(VIRTUAL_HOLD, ¶ms);
00216 return;
00217 }
00218
00219 ref();
00220 {
00221 QByteArray data;
00222 QDataStream stream( data, IO_WriteOnly );
00223 stream << url;
00224 slaveconn.send( CMD_SLAVE_HOLD, data );
00225 slaveconn.close();
00226 dead = true;
00227 emit slaveDied(this);
00228 }
00229 deref();
00230
00231 {
00232 DCOPClient *client = kapp->dcopClient();
00233 if (!client->isAttached())
00234 client->attach();
00235
00236 QByteArray params, reply;
00237 QCString replyType;
00238 QDataStream stream(params, IO_WriteOnly);
00239 pid_t pid = m_pid;
00240 stream << pid;
00241
00242 QCString launcher = KApplication::launcher();
00243 client->call(launcher, launcher, "waitForSlave(pid_t)",
00244 params, replyType, reply);
00245 }
00246 }
00247
00248 void Slave::suspend()
00249 {
00250 if (d->derived) {
00251 virtual_hook(VIRTUAL_SUSPEND, 0);
00252 return;
00253 }
00254
00255 slaveconn.suspend();
00256 }
00257
00258 void Slave::resume()
00259 {
00260 if (d->derived) {
00261 virtual_hook(VIRTUAL_RESUME, 0);
00262 return;
00263 }
00264
00265 slaveconn.resume();
00266 }
00267
00268 bool Slave::suspended()
00269 {
00270 if (d->derived) {
00271 SuspendedParams params;
00272 virtual_hook(VIRTUAL_SUSPENDED, ¶ms);
00273 return params.retval;
00274 }
00275
00276 return slaveconn.suspended();
00277 }
00278
00279 void Slave::send(int cmd, const QByteArray &arr) {
00280 if (d->derived) {
00281 SendParams params;
00282 params.cmd = cmd;
00283 params.arr = &arr;
00284 virtual_hook(VIRTUAL_SEND, ¶ms);
00285 return;
00286 }
00287
00288 slaveconn.send(cmd, arr);
00289 }
00290
00291 void Slave::gotInput()
00292 {
00293 ref();
00294 if (!dispatch())
00295 {
00296 slaveconn.close();
00297 dead = true;
00298 QString arg = m_protocol;
00299 if (!m_host.isEmpty())
00300 arg += "://"+m_host;
00301 kdDebug(7002) << "slave died pid = " << m_pid << endl;
00302
00303 emit error(ERR_SLAVE_DIED, arg);
00304
00305 emit slaveDied(this);
00306
00307 }
00308 deref();
00309 }
00310
00311 void Slave::kill()
00312 {
00313 dead = true;
00314 kdDebug(7002) << "killing slave pid=" << m_pid << " (" << m_protocol << "://"
00315 << m_host << ")" << endl;
00316 if (m_pid)
00317 {
00318 ::kill(m_pid, SIGTERM);
00319 }
00320 }
00321
00322 void Slave::setHost( const QString &host, int port,
00323 const QString &user, const QString &passwd)
00324 {
00325 m_host = host;
00326 m_port = port;
00327 m_user = user;
00328 m_passwd = passwd;
00329
00330 QByteArray data;
00331 QDataStream stream( data, IO_WriteOnly );
00332 stream << m_host << m_port << m_user << m_passwd;
00333 slaveconn.send( CMD_HOST, data );
00334 }
00335
00336 void Slave::resetHost()
00337 {
00338 m_host = "<reset>";
00339 }
00340
00341 void Slave::setConfig(const MetaData &config)
00342 {
00343 QByteArray data;
00344 QDataStream stream( data, IO_WriteOnly );
00345 stream << config;
00346 slaveconn.send( CMD_CONFIG, data );
00347 }
00348
00349 Slave* Slave::createSlave( const QString &protocol, const KURL& url, int& error, QString& error_text )
00350 {
00351
00352
00353 if (protocol == "data")
00354 return new DataProtocol();
00355
00356 DCOPClient *client = kapp->dcopClient();
00357 if (!client->isAttached())
00358 client->attach();
00359
00360 QString prefix = locateLocal("socket", KGlobal::instance()->instanceName());
00361 KTempFile socketfile(prefix, QString::fromLatin1(".slave-socket"));
00362 if ( socketfile.status() != 0 )
00363 {
00364 error_text = i18n("Unable to create io-slave: %1").arg(strerror(errno));
00365 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00366 return 0;
00367 }
00368 KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
00369
00370 Slave *slave = new Slave(kss, protocol, socketfile.name());
00371
00372
00373
00374
00375
00376
00377
00378
00379 static bool bForkSlaves = !QCString(getenv("KDE_FORK_SLAVES")).isEmpty();
00380
00381 if (bForkSlaves || !client->isAttached() || client->isAttachedToForeignServer())
00382 {
00383 QString _name = KProtocolInfo::exec(protocol);
00384 if (_name.isEmpty())
00385 {
00386 error_text = i18n("Unknown protocol '%1'.").arg(protocol);
00387 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00388 delete slave;
00389 return 0;
00390 }
00391 QString lib_path = KLibLoader::findLibrary(_name.latin1());
00392 if (lib_path.isEmpty())
00393 {
00394 error_text = i18n("Can not find io-slave for protocol '%1'.").arg(protocol);
00395 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00396 return 0;
00397 }
00398
00399 KProcess proc;
00400
00401 proc << locate("exe", "kioslave") << lib_path << protocol << "" << socketfile.name();
00402 kdDebug(7002) << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString::null << ", " << socketfile.name() << endl;
00403
00404 proc.start(KProcess::DontCare);
00405
00406 slave->setPID(proc.pid());
00407 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00408 return slave;
00409 }
00410
00411
00412 QByteArray params, reply;
00413 QCString replyType;
00414 QDataStream stream(params, IO_WriteOnly);
00415 stream << protocol << url.host() << socketfile.name();
00416
00417 QCString launcher = KApplication::launcher();
00418 if (!client->call(launcher, launcher, "requestSlave(QString,QString,QString)",
00419 params, replyType, reply)) {
00420 error_text = i18n("Cannot talk to klauncher");
00421 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00422 delete slave;
00423 return 0;
00424 }
00425 QDataStream stream2(reply, IO_ReadOnly);
00426 QString errorStr;
00427 pid_t pid;
00428 stream2 >> pid >> errorStr;
00429 if (!pid)
00430 {
00431 error_text = i18n("Unable to create io-slave:\nklauncher said: %1").arg(errorStr);
00432 error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00433 delete slave;
00434 return 0;
00435 }
00436 slave->setPID(pid);
00437 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00438
00439 return slave;
00440 }
00441
00442 Slave* Slave::holdSlave( const QString &protocol, const KURL& url )
00443 {
00444
00445
00446 if (protocol == "data")
00447 return new DataProtocol();
00448
00449 DCOPClient *client = kapp->dcopClient();
00450 if (!client->isAttached())
00451 client->attach();
00452
00453 QString prefix = locateLocal("socket", KGlobal::instance()->instanceName());
00454 KTempFile socketfile(prefix, QString::fromLatin1(".slave-socket"));
00455 if ( socketfile.status() != 0 )
00456 return 0;
00457
00458 KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
00459
00460 Slave *slave = new Slave(kss, protocol, socketfile.name());
00461
00462 QByteArray params, reply;
00463 QCString replyType;
00464 QDataStream stream(params, IO_WriteOnly);
00465 stream << url << socketfile.name();
00466
00467 QCString launcher = KApplication::launcher();
00468 if (!client->call(launcher, launcher, "requestHoldSlave(KURL,QString)",
00469 params, replyType, reply)) {
00470 delete slave;
00471 return 0;
00472 }
00473 QDataStream stream2(reply, IO_ReadOnly);
00474 pid_t pid;
00475 stream2 >> pid;
00476 if (!pid)
00477 {
00478 delete slave;
00479 return 0;
00480 }
00481 slave->setPID(pid);
00482 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00483
00484 return slave;
00485 }
00486
00487 void Slave::virtual_hook( int id, void* data ) {
00488 KIO::SlaveInterface::virtual_hook( id, data );
00489 }
00490
00491 #include "slave.moc"