kio Library API Documentation

slave.cpp

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 * $Id: slave.cpp,v 1.61.2.1 2004/09/08 12:36:20 tilladam Exp $ 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Library General Public 00010 * License version 2 as published by the Free Software Foundation. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Library General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Library General Public License 00018 * along with this library; see the file COPYING.LIB. If not, write to 00019 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00020 * Boston, MA 02111-1307, USA. 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 // Without debug info we consider it an error if the slave doesn't connect 00067 // within 10 seconds. 00068 // With debug info we give the slave an hour so that developers have a chance 00069 // to debug their slave. 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; // true if this instance of Slave is actually an 00084 // instance of a derived class. 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 // Tell the job about the problem. 00132 emit error(ERR_SLAVE_DIED, arg); 00133 // Tell the scheduler about the problem. 00134 emit slaveDied(this); 00135 // After the above signal we're dead!! 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 /*derived*/, KServerSocket *socket, const QString &protocol, 00157 const QString &socketname) 00158 : SlaveInterface(&slaveconn), serv(socket), contacted(false), 00159 d(new SlavePrivate(true)) 00160 { 00161 // FIXME: hmm, duplicating code here from public ctor, no good (LS) 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 // kdDebug(7002) << "destructing slave object pid = " << m_pid << endl; 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) { // TODO: clean up before KDE 4 00213 HoldParams params; 00214 params.url = &url; 00215 virtual_hook(VIRTUAL_HOLD, &params); 00216 return; 00217 }/*end if*/ 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 // Call KLauncher::waitForSlave(pid); 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) { // TODO: clean up before KDE 4 00251 virtual_hook(VIRTUAL_SUSPEND, 0); 00252 return; 00253 }/*end if*/ 00254 00255 slaveconn.suspend(); 00256 } 00257 00258 void Slave::resume() 00259 { 00260 if (d->derived) { // TODO: clean up before KDE 4 00261 virtual_hook(VIRTUAL_RESUME, 0); 00262 return; 00263 }/*end if*/ 00264 00265 slaveconn.resume(); 00266 } 00267 00268 bool Slave::suspended() 00269 { 00270 if (d->derived) { // TODO: clean up before KDE 4 00271 SuspendedParams params; 00272 virtual_hook(VIRTUAL_SUSPENDED, &params); 00273 return params.retval; 00274 }/*end if*/ 00275 00276 return slaveconn.suspended(); 00277 } 00278 00279 void Slave::send(int cmd, const QByteArray &arr) { 00280 if (d->derived) { // TODO: clean up before KDE 4 00281 SendParams params; 00282 params.cmd = cmd; 00283 params.arr = &arr; 00284 virtual_hook(VIRTUAL_SEND, &params); 00285 return; 00286 }/*end if*/ 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 // Tell the job about the problem. 00303 emit error(ERR_SLAVE_DIED, arg); 00304 // Tell the scheduler about the problem. 00305 emit slaveDied(this); 00306 // After the above signal we're dead!! 00307 } 00308 deref(); 00309 } 00310 00311 void Slave::kill() 00312 { 00313 dead = true; // OO can be such simple. 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 //kdDebug(7002) << "createSlave '" << protocol << "' for " << url.prettyURL() << endl; 00352 // Firstly take into account all special slaves 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 // WABA: if the dcopserver is running under another uid we don't ask 00373 // klauncher for a slave, because the slave might have that other uid 00374 // as well, which might either be a) undesired or b) make it impossible 00375 // for the slave to connect to the application. 00376 // In such case we start the slave via KProcess. 00377 // It's possible to force this by setting the env. variable 00378 // KDE_FORK_SLAVES, Clearcase seems to require this. 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 //kdDebug(7002) << "holdSlave '" << protocol << "' for " << url.prettyURL() << endl; 00445 // Firstly take into account all special slaves 00446 if (protocol == "data") 00447 return 0; 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"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:29:31 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003