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