kio Library: slave.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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 344956 2004-09-08 12:39:04Z tilladam $
    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 #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    // Tell the job about the problem.
    00134    emit error(ERR_SLAVE_DIED, arg);
    00135    // Tell the scheduler about the problem.
    00136    emit slaveDied(this);
    00137    // After the above signal we're dead!!
    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 /*derived*/, KServerSocket *socket, const QString &protocol,
    00161     const QString &socketname)
    00162   : SlaveInterface(&slaveconn), serv(socket), contacted(false),
    00163     d(new SlavePrivate(true))
    00164 {
    00165     // FIXME: hmm, duplicating code here from public ctor, no good (LS)
    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     // kdDebug(7002) << "destructing slave object pid = " << m_pid << endl;
    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) {        // TODO: clean up before KDE 4
    00219      HoldParams params;
    00220      params.url = &url;
    00221      virtual_hook(VIRTUAL_HOLD, &params);
    00222      return;
    00223    }/*end if*/
    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    // Call KLauncher::waitForSlave(pid);
    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) {        // TODO: clean up before KDE 4
    00257      virtual_hook(VIRTUAL_SUSPEND, 0);
    00258      return;
    00259    }/*end if*/
    00260
    00261    slaveconn.suspend();
    00262 }
    00263
    00264 void Slave::resume()
    00265 {
    00266    if (d->derived) {        // TODO: clean up before KDE 4
    00267      virtual_hook(VIRTUAL_RESUME, 0);
    00268      return;
    00269    }/*end if*/
    00270
    00271    slaveconn.resume();
    00272 }
    00273
    00274 bool Slave::suspended()
    00275 {
    00276    if (d->derived) {        // TODO: clean up before KDE 4
    00277      SuspendedParams params;
    00278      virtual_hook(VIRTUAL_SUSPENDED, &params);
    00279      return params.retval;
    00280    }/*end if*/
    00281
    00282    return slaveconn.suspended();
    00283 }
    00284
    00285 void Slave::send(int cmd, const QByteArray &arr) {
    00286    if (d->derived) {        // TODO: clean up before KDE 4
    00287      SendParams params;
    00288      params.cmd = cmd;
    00289      params.arr = &arr;
    00290      virtual_hook(VIRTUAL_SEND, &params);
    00291      return;
    00292    }/*end if*/
    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         // Tell the job about the problem.
    00309         emit error(ERR_SLAVE_DIED, arg);
    00310         // Tell the scheduler about the problem.
    00311         emit slaveDied(this);
    00312         // After the above signal we're dead!!
    00313     }
    00314     deref();
    00315 }
    00316
    00317 void Slave::kill()
    00318 {
    00319     dead = true; // OO can be such simple.
    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     //kdDebug(7002) << "createSlave '" << protocol << "' for " << url.prettyURL() << endl;
    00358     // Firstly take into account all special slaves
    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 #ifndef Q_WS_WIN
    00375     KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
    00376
    00377     Slave *slave = new Slave(kss, protocol, socketfile.name());
    00378 #else
    00379     Slave *slave = 0;
    00380 #endif
    00381
    00382     // WABA: if the dcopserver is running under another uid we don't ask
    00383     // klauncher for a slave, because the slave might have that other uid
    00384     // as well, which might either be a) undesired or b) make it impossible
    00385     // for the slave to connect to the application.
    00386     // In such case we start the slave via KProcess.
    00387     // It's possible to force this by setting the env. variable
    00388     // KDE_FORK_SLAVES, Clearcase seems to require this.
    00389     static bool bForkSlaves = !QCString(getenv("KDE_FORK_SLAVES")).isEmpty();
    00390
    00391     if (bForkSlaves || !client->isAttached() || client->isAttachedToForeignServer())
    00392     {
    00393        QString _name = KProtocolInfo::exec(protocol);
    00394        if (_name.isEmpty())
    00395        {
    00396           error_text = i18n("Unknown protocol '%1'.").arg(protocol);
    00397           error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
    00398           delete slave;
    00399           return 0;
    00400        }
    00401        QString lib_path = KLibLoader::findLibrary(_name.latin1());
    00402        if (lib_path.isEmpty())
    00403        {
    00404           error_text = i18n("Can not find io-slave for protocol '%1'.").arg(protocol);
    00405           error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
    00406           return 0;
    00407        }
    00408
    00409        KProcess proc;
    00410
    00411        proc << locate("exe", "kioslave") << lib_path << protocol << "" << socketfile.name();
    00412        kdDebug(7002) << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString::null << ", " << socketfile.name() << endl;
    00413
    00414        proc.start(KProcess::DontCare);
    00415
    00416 #ifndef Q_WS_WIN
    00417        slave->setPID(proc.pid());
    00418        QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
    00419 #endif
    00420        return slave;
    00421     }
    00422
    00423
    00424     QByteArray params, reply;
    00425     QCString replyType;
    00426     QDataStream stream(params, IO_WriteOnly);
    00427     stream << protocol << url.host() << socketfile.name();
    00428
    00429     QCString launcher = KApplication::launcher();
    00430     if (!client->call(launcher, launcher, "requestSlave(QString,QString,QString)",
    00431         params, replyType, reply)) {
    00432     error_text = i18n("Cannot talk to klauncher");
    00433     error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
    00434         delete slave;
    00435         return 0;
    00436     }
    00437     QDataStream stream2(reply, IO_ReadOnly);
    00438     QString errorStr;
    00439     pid_t pid;
    00440     stream2 >> pid >> errorStr;
    00441     if (!pid)
    00442     {
    00443         error_text = i18n("Unable to create io-slave:\nklauncher said: %1").arg(errorStr);
    00444         error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
    00445         delete slave;
    00446         return 0;
    00447     }
    00448 #ifndef Q_WS_WIN
    00449     slave->setPID(pid);
    00450     QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
    00451 #endif
    00452     return slave;
    00453 }
    00454
    00455 Slave* Slave::holdSlave( const QString &protocol, const KURL& url )
    00456 {
    00457     //kdDebug(7002) << "holdSlave '" << protocol << "' for " << url.prettyURL() << endl;
    00458     // Firstly take into account all special slaves
    00459     if (protocol == "data")
    00460         return 0;
    00461
    00462     DCOPClient *client = kapp->dcopClient();
    00463     if (!client->isAttached())
    00464     client->attach();
    00465
    00466     QString prefix = locateLocal("socket", KGlobal::instance()->instanceName());
    00467     KTempFile socketfile(prefix, QString::fromLatin1(".slave-socket"));
    00468     if ( socketfile.status() != 0 )
    00469     return 0;
    00470
    00471 #ifndef Q_WS_WIN
    00472     KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
    00473
    00474     Slave *slave = new Slave(kss, protocol, socketfile.name());
    00475 #else
    00476     Slave *slave = 0;
    00477 #endif
    00478
    00479     QByteArray params, reply;
    00480     QCString replyType;
    00481     QDataStream stream(params, IO_WriteOnly);
    00482     stream << url << socketfile.name();
    00483
    00484     QCString launcher = KApplication::launcher();
    00485     if (!client->call(launcher, launcher, "requestHoldSlave(KURL,QString)",
    00486         params, replyType, reply)) {
    00487         delete slave;
    00488         return 0;
    00489     }
    00490     QDataStream stream2(reply, IO_ReadOnly);
    00491     pid_t pid;
    00492     stream2 >> pid;
    00493     if (!pid)
    00494     {
    00495         delete slave;
    00496         return 0;
    00497     }
    00498 #ifndef Q_WS_WIN
    00499     slave->setPID(pid);
    00500     QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
    00501 #endif
    00502     return slave;
    00503 }
    00504
    00505 void Slave::virtual_hook( int id, void* data ) {
    00506   KIO::SlaveInterface::virtual_hook( id, data );
    00507 }
    00508
    00509 #include "slave.moc"

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:07 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003