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 Sat Jan 22 16:47:18 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003