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.56 2002/06/19 17:46:02 domi 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/slave.h"
00048 #include "kio/kservice.h"
00049 #include <kio/global.h>
00050 #include <kprotocolmanager.h>
00051 #include <kprotocolinfo.h>
00052 
00053 #ifdef HAVE_PATHS_H
00054 #include <paths.h>
00055 #endif
00056 
00057 #ifndef _PATH_TMP
00058 #define _PATH_TMP "/tmp"
00059 #endif
00060 
00061 using namespace KIO;
00062 
00063 #define SLAVE_CONNECTION_TIMEOUT_MIN       2
00064 
00065 // Without debug info we consider it an error if the slave doesn't connect
00066 // within 10 seconds.
00067 // With debug info we give the slave an hour so that developers have a chance
00068 // to debug their slave.
00069 #ifdef NDEBUG
00070 #define SLAVE_CONNECTION_TIMEOUT_MAX      10
00071 #else
00072 #define SLAVE_CONNECTION_TIMEOUT_MAX    3600
00073 #endif
00074 
00075 void Slave::accept(KSocket *socket)
00076 {
00077     slaveconn.init(socket);
00078     delete serv;
00079     serv = 0;
00080     slaveconn.connect(this, SLOT(gotInput()));
00081     unlinkSocket();
00082 }
00083 
00084 void Slave::unlinkSocket()
00085 {
00086     if (m_socket.isEmpty()) return;
00087     QCString filename = QFile::encodeName(m_socket);
00088     unlink(filename.data());
00089     m_socket = QString::null;
00090 }
00091 
00092 void Slave::timeout()
00093 {
00094    if (!serv) return;
00095    kdDebug(7002) << "slave failed to connect to application pid=" << m_pid << " protocol=" << m_protocol << endl;
00096    if (m_pid && (::kill(m_pid, 0) == 0))
00097    {
00098       int delta_t = (int) difftime(time(0), contact_started);
00099       kdDebug(7002) << "slave is slow... pid=" << m_pid << " t=" << delta_t << endl;
00100       if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX)
00101       {
00102          QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, SLOT(timeout()));
00103          return;
00104       }
00105    }
00106    kdDebug(7002) << "Houston, we lost our slave, pid=" << m_pid << endl;
00107    delete serv;
00108    serv = 0;
00109    unlinkSocket();
00110    dead = true;
00111    QString arg = m_protocol;
00112    if (!m_host.isEmpty())
00113       arg += "://"+m_host;
00114    kdDebug(7002) << "slave died pid = " << m_pid << endl;
00115    ref();
00116    // Tell the job about the problem.
00117    emit error(ERR_SLAVE_DIED, arg);
00118    // Tell the scheduler about the problem.
00119    emit slaveDied(this);
00120    // After the above signal we're dead!!
00121    deref();
00122 }
00123 
00124 Slave::Slave(KServerSocket *socket, const QString &protocol, const QString &socketname)
00125   : SlaveInterface(&slaveconn), serv(socket), contacted(false)
00126 {
00127     m_refCount = 1;
00128     m_protocol = protocol;
00129     m_slaveProtocol = protocol;
00130     m_socket = socketname;
00131     dead = false;
00132     contact_started = time(0);
00133     idle_since = contact_started;
00134     m_pid = 0;
00135     m_port = 0;
00136     connect(serv, SIGNAL(accepted( KSocket* )),
00137             SLOT(accept(KSocket*) ) );
00138 }
00139 
00140 Slave::~Slave()
00141 {
00142     // kdDebug(7002) << "destructing slave object pid = " << m_pid << endl;
00143     if (serv != 0) {
00144         delete serv;
00145         serv = 0;
00146     }
00147     unlinkSocket();
00148     m_pid = 99999;
00149 }
00150 
00151 void Slave::setProtocol(const QString & protocol)
00152 {
00153     m_protocol = protocol;
00154 }
00155 
00156 void Slave::setIdle()
00157 {
00158     idle_since = time(0);
00159 }
00160 
00161 time_t Slave::idleTime()
00162 {
00163     return (time_t) difftime(time(0), idle_since);
00164 }
00165 
00166 void Slave::setPID(pid_t pid)
00167 {
00168     m_pid = pid;
00169 }
00170 
00171 void Slave::hold(const KURL &url)
00172 {
00173    ref();
00174    {
00175       QByteArray data;
00176       QDataStream stream( data, IO_WriteOnly );
00177       stream << url;
00178       slaveconn.send( CMD_SLAVE_HOLD, data );
00179       slaveconn.close();
00180       dead = true;
00181       emit slaveDied(this);
00182    }
00183    deref();
00184    // Call KLauncher::waitForSlave(pid);
00185    {
00186       DCOPClient *client = kapp->dcopClient();
00187       if (!client->isAttached())
00188          client->attach();
00189 
00190       QByteArray params, reply;
00191       QCString replyType;
00192       QDataStream stream(params, IO_WriteOnly);
00193       pid_t pid = m_pid;
00194       stream << pid;
00195 
00196       QCString launcher = KApplication::launcher();
00197       client->call(launcher, launcher, "waitForSlave(pid_t)",
00198             params, replyType, reply);
00199    }
00200 }
00201 
00202 void Slave::suspend()
00203 {
00204    slaveconn.suspend();
00205 }
00206 
00207 void Slave::resume()
00208 {
00209    slaveconn.resume();
00210 }
00211 
00212 bool Slave::suspended()
00213 {
00214    return slaveconn.suspended();
00215 }
00216 
00217 
00218 void Slave::gotInput()
00219 {
00220     ref();
00221     if (!dispatch())
00222     {
00223         slaveconn.close();
00224         dead = true;
00225         QString arg = m_protocol;
00226         if (!m_host.isEmpty())
00227             arg += "://"+m_host;
00228         kdDebug(7002) << "slave died pid = " << m_pid << endl;
00229         // Tell the job about the problem.
00230         emit error(ERR_SLAVE_DIED, arg);
00231         // Tell the scheduler about the problem.
00232         emit slaveDied(this);
00233         // After the above signal we're dead!!
00234     }
00235     deref();
00236 }
00237 
00238 void Slave::kill()
00239 {
00240     dead = true; // OO can be such simple.
00241     kdDebug(7002) << "killing slave pid=" << m_pid << " (" << m_protocol << "://"
00242                   << m_host << ")" << endl;
00243     if (m_pid)
00244     {
00245        ::kill(m_pid, SIGTERM);
00246     }
00247 }
00248 
00249 void Slave::setHost( const QString &host, int port,
00250                      const QString &user, const QString &passwd)
00251 {
00252     m_host = host;
00253     m_port = port;
00254     m_user = user;
00255     m_passwd = passwd;
00256 
00257     QByteArray data;
00258     QDataStream stream( data, IO_WriteOnly );
00259     stream << m_host << m_port << m_user << m_passwd;
00260     slaveconn.send( CMD_HOST, data );
00261 }
00262 
00263 void Slave::resetHost()
00264 {
00265     m_host = "<reset>";
00266 }
00267 
00268 void Slave::setConfig(const MetaData &config)
00269 {
00270     QByteArray data;
00271     QDataStream stream( data, IO_WriteOnly );
00272     stream << config;
00273     slaveconn.send( CMD_CONFIG, data );
00274 }
00275 
00276 Slave* Slave::createSlave( const QString &protocol, const KURL& url, int& error, QString& error_text )
00277 {
00278     //kdDebug(7002) << "createSlave '" << protocol << "' for " << url.prettyURL() << endl;
00279 
00280     DCOPClient *client = kapp->dcopClient();
00281     if (!client->isAttached())
00282         client->attach();
00283 
00284     QString prefix = locateLocal("socket", KGlobal::instance()->instanceName());
00285     KTempFile socketfile(prefix, QString::fromLatin1(".slave-socket"));
00286     if ( socketfile.status() != 0 )
00287     {
00288         error_text = i18n("Unable to create io-slave: %1").arg(strerror(errno));
00289         error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00290         return 0;
00291     }
00292     KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
00293 
00294     Slave *slave = new Slave(kss, protocol, socketfile.name());
00295 
00296     // WABA: if the dcopserver is running under another uid we don't ask 
00297     // klauncher for a slave, because the slave might have that other uid 
00298     // as well, which might either be a) undesired or b) make it impossible 
00299     // for the slave to connect to the application.
00300     // In such case we start the slave via KProcess.
00301     if (!client->isAttached() || client->isAttachedToForeignServer())
00302     {
00303        QString _name = KProtocolInfo::exec(protocol);
00304        if (_name.isEmpty())
00305        {
00306           error_text = i18n("Unknown protocol '%1'.\n").arg(protocol);
00307           error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00308           delete slave;
00309           return 0;
00310        }
00311        QString lib_path = KLibLoader::findLibrary(_name.latin1());
00312        if (lib_path.isEmpty())
00313        {
00314           error_text = i18n("Can not find io-slave for protocol '%1'.").arg(protocol);
00315           error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00316           return 0;
00317        }
00318 
00319        KProcess proc;
00320        
00321        proc << "kioslave" << lib_path << protocol << "" << socketfile.name();
00322        kdDebug(7002) << "kioslave" << ", " << lib_path << ", " << protocol << ", " << QString::null << ", " << socketfile.name() << endl;
00323 
00324        proc.start(KProcess::DontCare);
00325 
00326        slave->setPID(proc.pid());
00327        QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00328        return slave;
00329     }
00330 
00331 
00332     QByteArray params, reply;
00333     QCString replyType;
00334     QDataStream stream(params, IO_WriteOnly);
00335     stream << protocol << url.host() << socketfile.name();
00336 
00337     QCString launcher = KApplication::launcher();
00338     if (!client->call(launcher, launcher, "requestSlave(QString,QString,QString)",
00339             params, replyType, reply)) {
00340         error_text = i18n("Can't talk to klauncher");
00341         error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00342         delete slave;
00343         return 0;
00344     }
00345     QDataStream stream2(reply, IO_ReadOnly);
00346     QString errorStr;
00347     pid_t pid;
00348     stream2 >> pid >> errorStr;
00349     if (!pid)
00350     {
00351         error_text = i18n("Unable to create io-slave:\nklauncher said: %1").arg(errorStr);
00352         error = KIO::ERR_CANNOT_LAUNCH_PROCESS;
00353         delete slave;
00354         return 0;
00355     }
00356     slave->setPID(pid);
00357     QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00358 
00359     return slave;
00360 }
00361 
00362 Slave* Slave::holdSlave( const QString &protocol, const KURL& url )
00363 {
00364     //kdDebug(7002) << "holdSlave '" << protocol << "' for " << url.prettyURL() << endl;
00365 
00366     DCOPClient *client = kapp->dcopClient();
00367     if (!client->isAttached())
00368         client->attach();
00369 
00370     QString prefix = locateLocal("socket", KGlobal::instance()->instanceName());
00371     KTempFile socketfile(prefix, QString::fromLatin1(".slave-socket"));
00372     if ( socketfile.status() != 0 )
00373         return 0;
00374 
00375     KServerSocket *kss = new KServerSocket(QFile::encodeName(socketfile.name()));
00376 
00377     Slave *slave = new Slave(kss, protocol, socketfile.name());
00378 
00379     QByteArray params, reply;
00380     QCString replyType;
00381     QDataStream stream(params, IO_WriteOnly);
00382     stream << url << socketfile.name();
00383 
00384     QCString launcher = KApplication::launcher();
00385     if (!client->call(launcher, launcher, "requestHoldSlave(KURL,QString)",
00386             params, replyType, reply)) {
00387         delete slave;
00388         return 0;
00389     }
00390     QDataStream stream2(reply, IO_ReadOnly);
00391     pid_t pid;
00392     stream2 >> pid;
00393     if (!pid)
00394     {
00395         delete slave;
00396         return 0;
00397     }
00398     slave->setPID(pid);
00399     QTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, SLOT(timeout()));
00400 
00401     return slave;
00402 }
00403 
00404 void Slave::virtual_hook( int id, void* data )
00405 { KIO::SlaveInterface::virtual_hook( id, data ); }
00406 
00407 #include "slave.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:38 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001