KIO
slave.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (c) 2000 Waldo Bastian <bastian@kde.org> 00004 * 2000 Stephan Kulow <coolo@kde.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 **/ 00020 00021 #include "slave.h" 00022 00023 #include <time.h> 00024 #include <errno.h> 00025 #include <unistd.h> 00026 #include <stdlib.h> 00027 #include <stdio.h> 00028 #include <signal.h> 00029 #include <sys/types.h> 00030 00031 #include <QtCore/QFile> 00032 #include <QtCore/QTimer> 00033 #include <QtDBus/QtDBus> 00034 #include <QtCore/QProcess> 00035 00036 #include <kdebug.h> 00037 #include <klocale.h> 00038 #include <kglobal.h> 00039 #include <kstandarddirs.h> 00040 #include <ktoolinvocation.h> 00041 #include <klauncher_iface.h> 00042 #include <klibrary.h> 00043 00044 #include "dataprotocol.h" 00045 #include "kservice.h" 00046 #include <kio/global.h> 00047 #include "kio/connection.h" 00048 #include <kprotocolinfo.h> 00049 00050 #include "slaveinterface_p.h" 00051 00052 using namespace KIO; 00053 00054 #define SLAVE_CONNECTION_TIMEOUT_MIN 2 00055 00056 // Without debug info we consider it an error if the slave doesn't connect 00057 // within 10 seconds. 00058 // With debug info we give the slave an hour so that developers have a chance 00059 // to debug their slave. 00060 #ifdef NDEBUG 00061 #define SLAVE_CONNECTION_TIMEOUT_MAX 10 00062 #else 00063 #define SLAVE_CONNECTION_TIMEOUT_MAX 3600 00064 #endif 00065 00066 namespace KIO { 00067 00071 class SlavePrivate: public SlaveInterfacePrivate 00072 { 00073 public: 00074 SlavePrivate(const QString &protocol) : 00075 m_protocol(protocol), 00076 m_slaveProtocol(protocol), 00077 slaveconnserver(new KIO::ConnectionServer), 00078 m_job(0), 00079 m_pid(0), 00080 m_port(0), 00081 contacted(false), 00082 dead(false), 00083 contact_started(time(0)), 00084 m_idleSince(0), 00085 m_refCount(1) 00086 { 00087 slaveconnserver->listenForRemote(); 00088 if ( !slaveconnserver->isListening() ) 00089 kWarning() << "Connection server not listening, could not connect"; 00090 } 00091 ~SlavePrivate() 00092 { 00093 delete slaveconnserver; 00094 } 00095 00096 QString m_protocol; 00097 QString m_slaveProtocol; 00098 QString m_host; 00099 QString m_user; 00100 QString m_passwd; 00101 KIO::ConnectionServer *slaveconnserver; 00102 KIO::SimpleJob *m_job; 00103 pid_t m_pid; 00104 quint16 m_port; 00105 bool contacted; 00106 bool dead; 00107 time_t contact_started; 00108 time_t m_idleSince; 00109 int m_refCount; 00110 }; 00111 } 00112 00113 void Slave::accept() 00114 { 00115 Q_D(Slave); 00116 d->slaveconnserver->setNextPendingConnection(d->connection); 00117 d->slaveconnserver->deleteLater(); 00118 d->slaveconnserver = 0; 00119 00120 connect(d->connection, SIGNAL(readyRead()), SLOT(gotInput())); 00121 } 00122 00123 void Slave::timeout() 00124 { 00125 Q_D(Slave); 00126 if (d->dead) //already dead? then slaveDied was emitted and we are done 00127 return; 00128 if (d->connection->isConnected()) 00129 return; 00130 00131 kDebug(7002) << "slave failed to connect to application pid=" << d->m_pid 00132 << " 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 00151 ref(); 00152 // Tell the job about the problem. 00153 emit error(ERR_SLAVE_DIED, arg); 00154 // Tell the scheduler about the problem. 00155 emit slaveDied(this); 00156 // After the above signal we're dead!! 00157 deref(); 00158 } 00159 00160 Slave::Slave(const QString &protocol, QObject *parent) 00161 : SlaveInterface(*new SlavePrivate(protocol), parent) 00162 { 00163 Q_D(Slave); 00164 d->slaveconnserver->setParent(this); 00165 d->connection = new Connection(this); 00166 connect(d->slaveconnserver, SIGNAL(newConnection()), SLOT(accept())); 00167 } 00168 00169 Slave::~Slave() 00170 { 00171 // kDebug(7002) << "destructing slave object pid = " << d->m_pid; 00172 //delete d; 00173 } 00174 00175 QString Slave::protocol() 00176 { 00177 Q_D(Slave); 00178 return d->m_protocol; 00179 } 00180 00181 void Slave::setProtocol(const QString & protocol) 00182 { 00183 Q_D(Slave); 00184 d->m_protocol = protocol; 00185 } 00186 00187 QString Slave::slaveProtocol() 00188 { 00189 Q_D(Slave); 00190 return d->m_slaveProtocol; 00191 } 00192 00193 QString Slave::host() 00194 { 00195 Q_D(Slave); 00196 return d->m_host; 00197 } 00198 00199 quint16 Slave::port() 00200 { 00201 Q_D(Slave); 00202 return d->m_port; 00203 } 00204 00205 QString Slave::user() 00206 { 00207 Q_D(Slave); 00208 return d->m_user; 00209 } 00210 00211 QString Slave::passwd() 00212 { 00213 Q_D(Slave); 00214 return d->m_passwd; 00215 } 00216 00217 void Slave::setIdle() 00218 { 00219 Q_D(Slave); 00220 d->m_idleSince = time(0); 00221 } 00222 00223 bool Slave::isConnected() 00224 { 00225 Q_D(Slave); 00226 return d->contacted; 00227 } 00228 00229 void Slave::setConnected(bool c) 00230 { 00231 Q_D(Slave); 00232 d->contacted = c; 00233 } 00234 00235 void Slave::ref() 00236 { 00237 Q_D(Slave); 00238 d->m_refCount++; 00239 } 00240 00241 void Slave::deref() 00242 { 00243 Q_D(Slave); 00244 d->m_refCount--; 00245 if (!d->m_refCount) { 00246 d->connection->disconnect(this); 00247 this->disconnect(); 00248 deleteLater(); 00249 } 00250 } 00251 00252 time_t Slave::idleTime() 00253 { 00254 Q_D(Slave); 00255 if (!d->m_idleSince) { 00256 return time_t(0); 00257 } 00258 return time_t(difftime(time(0), d->m_idleSince)); 00259 } 00260 00261 void Slave::setPID(pid_t pid) 00262 { 00263 Q_D(Slave); 00264 d->m_pid = pid; 00265 } 00266 00267 int Slave::slave_pid() 00268 { 00269 Q_D(Slave); 00270 return d->m_pid; 00271 } 00272 00273 void Slave::setJob(KIO::SimpleJob *job) 00274 { 00275 Q_D(Slave); 00276 if (!d->sslMetaData.isEmpty()) { 00277 emit metaData(d->sslMetaData); 00278 } 00279 d->m_job = job; 00280 } 00281 00282 KIO::SimpleJob *Slave::job() const 00283 { 00284 Q_D(const Slave); 00285 return d->m_job; 00286 } 00287 00288 bool Slave::isAlive() 00289 { 00290 Q_D(Slave); 00291 return !d->dead; 00292 } 00293 00294 void Slave::hold(const KUrl &url) 00295 { 00296 Q_D(Slave); 00297 ref(); 00298 { 00299 QByteArray data; 00300 QDataStream stream( &data, QIODevice::WriteOnly ); 00301 stream << url; 00302 d->connection->send( CMD_SLAVE_HOLD, data ); 00303 d->connection->close(); 00304 d->dead = true; 00305 emit slaveDied(this); 00306 } 00307 deref(); 00308 // Call KLauncher::waitForSlave(pid); 00309 { 00310 KToolInvocation::klauncher()->waitForSlave(d->m_pid); 00311 } 00312 } 00313 00314 void Slave::suspend() 00315 { 00316 Q_D(Slave); 00317 d->connection->suspend(); 00318 } 00319 00320 void Slave::resume() 00321 { 00322 Q_D(Slave); 00323 d->connection->resume(); 00324 } 00325 00326 bool Slave::suspended() 00327 { 00328 Q_D(Slave); 00329 return d->connection->suspended(); 00330 } 00331 00332 void Slave::send(int cmd, const QByteArray &arr) 00333 { 00334 Q_D(Slave); 00335 d->connection->send(cmd, arr); 00336 } 00337 00338 void Slave::gotInput() 00339 { 00340 Q_D(Slave); 00341 if (d->dead) //already dead? then slaveDied was emitted and we are done 00342 return; 00343 ref(); 00344 if (!dispatch()) 00345 { 00346 d->connection->close(); 00347 d->dead = true; 00348 QString arg = d->m_protocol; 00349 if (!d->m_host.isEmpty()) 00350 arg += "://"+d->m_host; 00351 kDebug(7002) << "slave died pid = " << d->m_pid; 00352 // Tell the job about the problem. 00353 emit error(ERR_SLAVE_DIED, arg); 00354 // Tell the scheduler about the problem. 00355 emit slaveDied(this); 00356 } 00357 deref(); 00358 // Here we might be dead!! 00359 } 00360 00361 void Slave::kill() 00362 { 00363 Q_D(Slave); 00364 d->dead = true; // OO can be such simple. 00365 kDebug(7002) << "killing slave pid" << d->m_pid 00366 << "(" << QString(d->m_protocol) + "://" + d->m_host << ")"; 00367 if (d->m_pid) 00368 { 00369 #ifndef _WIN32_WCE 00370 ::kill(d->m_pid, SIGTERM); 00371 #else 00372 ::kill(d->m_pid, SIGKILL); 00373 #endif 00374 d->m_pid = 0; 00375 } 00376 } 00377 00378 void Slave::setHost( const QString &host, quint16 port, 00379 const QString &user, const QString &passwd) 00380 { 00381 Q_D(Slave); 00382 d->m_host = host; 00383 d->m_port = port; 00384 d->m_user = user; 00385 d->m_passwd = passwd; 00386 d->sslMetaData.clear(); 00387 00388 QByteArray data; 00389 QDataStream stream( &data, QIODevice::WriteOnly ); 00390 stream << d->m_host << d->m_port << d->m_user << d->m_passwd; 00391 d->connection->send( CMD_HOST, data ); 00392 } 00393 00394 void Slave::resetHost() 00395 { 00396 Q_D(Slave); 00397 d->sslMetaData.clear(); 00398 d->m_host = "<reset>"; 00399 } 00400 00401 void Slave::setConfig(const MetaData &config) 00402 { 00403 Q_D(Slave); 00404 QByteArray data; 00405 QDataStream stream( &data, QIODevice::WriteOnly ); 00406 stream << config; 00407 d->connection->send( CMD_CONFIG, data ); 00408 } 00409 00410 Slave* Slave::createSlave( const QString &protocol, const KUrl& url, int& error, QString& error_text ) 00411 { 00412 kDebug(7002) << "createSlave" << protocol << "for" << url; 00413 // Firstly take into account all special slaves 00414 if (protocol == "data") 00415 return new DataProtocol(); 00416 Slave *slave = new Slave(protocol); 00417 QString slaveAddress = slave->d_func()->slaveconnserver->address(); 00418 00419 #ifdef Q_OS_UNIX 00420 // In such case we start the slave via QProcess. 00421 // It's possible to force this by setting the env. variable 00422 // KDE_FORK_SLAVES, Clearcase seems to require this. 00423 static bool bForkSlaves = !qgetenv("KDE_FORK_SLAVES").isEmpty(); 00424 00425 if (!bForkSlaves) 00426 { 00427 // check the UID of klauncher 00428 QDBusReply<uint> reply = QDBusConnection::sessionBus().interface()->serviceUid(KToolInvocation::klauncher()->service()); 00429 if (reply.isValid() && getuid() != reply) 00430 bForkSlaves = true; 00431 } 00432 00433 if (bForkSlaves) 00434 { 00435 QString _name = KProtocolInfo::exec(protocol); 00436 if (_name.isEmpty()) 00437 { 00438 error_text = i18n("Unknown protocol '%1'.", protocol); 00439 error = KIO::ERR_CANNOT_LAUNCH_PROCESS; 00440 delete slave; 00441 return 0; 00442 } 00443 KLibrary lib(_name, KGlobal::mainComponent()); 00444 QString lib_path = lib.fileName(); 00445 if (lib_path.isEmpty()) 00446 { 00447 error_text = i18n("Can not find io-slave for protocol '%1'.", protocol); 00448 error = KIO::ERR_CANNOT_LAUNCH_PROCESS; 00449 delete slave; 00450 return 0; 00451 } 00452 00453 const QStringList args = QStringList() << lib_path << protocol << "" << slaveAddress; 00454 kDebug() << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString() << ", " << slaveAddress; 00455 00456 QProcess::startDetached( KStandardDirs::locate("exe", "kioslave"), args ); 00457 00458 return slave; 00459 } 00460 #endif 00461 00462 org::kde::KLauncher* klauncher = KToolInvocation::klauncher(); 00463 QString errorStr; 00464 QDBusReply<int> reply = klauncher->requestSlave(protocol, url.host(), slaveAddress, errorStr); 00465 if (!reply.isValid()) { 00466 error_text = i18n("Cannot talk to klauncher: %1", klauncher->lastError().message() ); 00467 error = KIO::ERR_CANNOT_LAUNCH_PROCESS; 00468 delete slave; 00469 return 0; 00470 } 00471 pid_t pid = reply; 00472 if (!pid) 00473 { 00474 error_text = i18n("Unable to create io-slave:\nklauncher said: %1", errorStr); 00475 error = KIO::ERR_CANNOT_LAUNCH_PROCESS; 00476 delete slave; 00477 return 0; 00478 } 00479 slave->setPID(pid); 00480 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); 00481 return slave; 00482 } 00483 00484 Slave* Slave::holdSlave( const QString &protocol, const KUrl& url ) 00485 { 00486 //kDebug(7002) << "holdSlave" << protocol << "for" << url; 00487 // Firstly take into account all special slaves 00488 if (protocol == "data") 00489 return 0; 00490 Slave *slave = new Slave(protocol); 00491 QString slaveAddress = slave->d_func()->slaveconnserver->address(); 00492 QDBusReply<int> reply = KToolInvocation::klauncher()->requestHoldSlave(url.url(), slaveAddress); 00493 if (!reply.isValid()) { 00494 delete slave; 00495 return 0; 00496 } 00497 pid_t pid = reply; 00498 if (!pid) 00499 { 00500 delete slave; 00501 return 0; 00502 } 00503 slave->setPID(pid); 00504 QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout())); 00505 return slave; 00506 } 00507 00508 bool Slave::checkForHeldSlave(const KUrl &url) 00509 { 00510 return KToolInvocation::klauncher()->checkForHeldSlave(url.url()); 00511 } 00512 00513 #include "slave.moc"
KDE 4.7 API Reference