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