diff options
Diffstat (limited to 'tdeio/tdeio/slave.cpp')
-rw-r--r-- | tdeio/tdeio/slave.cpp | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/tdeio/tdeio/slave.cpp b/tdeio/tdeio/slave.cpp new file mode 100644 index 000000000..dfd6d6bb3 --- /dev/null +++ b/tdeio/tdeio/slave.cpp @@ -0,0 +1,519 @@ +/* + * This file is part of the KDE libraries + * Copyright (c) 2000 Waldo Bastian <bastian@kde.org> + * 2000 Stephan Kulow <coolo@kde.org> + * + * $Id$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#include <config.h> + +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> + +#include <tqfile.h> +#include <tqtimer.h> + +#include <dcopclient.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kapplication.h> +#include <ktempfile.h> +#include <ksock.h> +#include <kprocess.h> +#include <klibloader.h> + +#include "tdeio/dataprotocol.h" +#include "tdeio/slave.h" +#include "tdeio/kservice.h" +#include <tdeio/global.h> +#include <kprotocolmanager.h> +#include <kprotocolinfo.h> + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#ifndef _PATH_TMP +#define _PATH_TMP "/tmp" +#endif + +using namespace TDEIO; + +#define SLAVE_CONNECTION_TIMEOUT_MIN 2 + +// Without debug info we consider it an error if the slave doesn't connect +// within 10 seconds. +// With debug info we give the slave an hour so that developers have a chance +// to debug their slave. +#ifdef NDEBUG +#define SLAVE_CONNECTION_TIMEOUT_MAX 10 +#else +#define SLAVE_CONNECTION_TIMEOUT_MAX 3600 +#endif + +namespace TDEIO { + + /** + * @internal + */ + class SlavePrivate { + public: + bool derived; // true if this instance of Slave is actually an + // instance of a derived class. + + SlavePrivate(bool derived) : derived(derived) {} + }; +} + +void Slave::accept(TDESocket *socket) +{ +#ifndef Q_WS_WIN + slaveconn.init(socket); +#endif + delete serv; + serv = 0; + slaveconn.connect(this, TQT_SLOT(gotInput())); + unlinkSocket(); +} + +void Slave::unlinkSocket() +{ + if (m_socket.isEmpty()) return; + TQCString filename = TQFile::encodeName(m_socket); + unlink(filename.data()); + m_socket = TQString::null; +} + +void Slave::timeout() +{ + if (!serv) return; + kdDebug(7002) << "slave failed to connect to application pid=" << m_pid << " protocol=" << m_protocol << endl; + if (m_pid && (::kill(m_pid, 0) == 0)) + { + int delta_t = (int) difftime(time(0), contact_started); + kdDebug(7002) << "slave is slow... pid=" << m_pid << " t=" << delta_t << endl; + if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX) + { + TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, TQT_SLOT(timeout())); + return; + } + } + kdDebug(7002) << "Houston, we lost our slave, pid=" << m_pid << endl; + delete serv; + serv = 0; + unlinkSocket(); + dead = true; + TQString arg = m_protocol; + if (!m_host.isEmpty()) + arg += "://"+m_host; + kdDebug(7002) << "slave died pid = " << m_pid << endl; + ref(); + // Tell the job about the problem. + emit error(ERR_SLAVE_DIED, arg); + // Tell the scheduler about the problem. + emit slaveDied(this); + // After the above signal we're dead!! + deref(); +} + +Slave::Slave(TDEServerSocket *socket, const TQString &protocol, const TQString &socketname) + : SlaveInterface(&slaveconn), serv(socket), contacted(false), + d(new SlavePrivate(false)) +{ + m_refCount = 1; + m_protocol = protocol; + m_slaveProtocol = protocol; + m_socket = socketname; + dead = false; + contact_started = time(0); + idle_since = contact_started; + m_pid = 0; + m_port = 0; +#ifndef Q_WS_WIN + connect(serv, TQT_SIGNAL(accepted( TDESocket* )), + TQT_SLOT(accept(TDESocket*) ) ); +#endif +} + +Slave::Slave(bool /*derived*/, TDEServerSocket *socket, const TQString &protocol, + const TQString &socketname) + : SlaveInterface(&slaveconn), serv(socket), contacted(false), + d(new SlavePrivate(true)) +{ + // FIXME: hmm, duplicating code here from public ctor, no good (LS) + m_refCount = 1; + m_protocol = protocol; + m_slaveProtocol = protocol; + m_socket = socketname; + dead = false; + contact_started = time(0); + idle_since = contact_started; + m_pid = 0; + m_port = 0; + if (serv != 0) { +#ifndef Q_WS_WIN + connect(serv, TQT_SIGNAL(accepted( TDESocket* )), + TQT_SLOT(accept(TDESocket*) ) ); +#endif + } +} + +Slave::~Slave() +{ + // kdDebug(7002) << "destructing slave object pid = " << m_pid << endl; + if (serv != 0) { + delete serv; + serv = 0; + } + unlinkSocket(); + m_pid = 99999; + delete d; + d = 0; +} + +void Slave::setProtocol(const TQString & protocol) +{ + m_protocol = protocol; +} + +void Slave::setIdle() +{ + idle_since = time(0); +} + +time_t Slave::idleTime() +{ + return (time_t) difftime(time(0), idle_since); +} + +void Slave::setPID(pid_t pid) +{ + m_pid = pid; +} + +void Slave::hold(const KURL &url) +{ + if (d->derived) { // TODO: clean up before KDE 4 + HoldParams params; + params.url = &url; + virtual_hook(VIRTUAL_HOLD, ¶ms); + return; + }/*end if*/ + + ref(); + { + TQByteArray data; + TQDataStream stream( data, IO_WriteOnly ); + stream << url; + slaveconn.send( CMD_SLAVE_HOLD, data ); + slaveconn.close(); + dead = true; + emit slaveDied(this); + } + deref(); + // Call KLauncher::waitForSlave(pid); + { + DCOPClient *client = kapp->dcopClient(); + if (!client->isAttached()) + client->attach(); + + TQByteArray params, reply; + TQCString replyType; + TQDataStream stream(params, IO_WriteOnly); + pid_t pid = m_pid; + stream << pid; + + TQCString launcher = TDEApplication::launcher(); + client->call(launcher, launcher, "waitForSlave(pid_t)", + params, replyType, reply); + } +} + +void Slave::suspend() +{ + if (d->derived) { // TODO: clean up before KDE 4 + virtual_hook(VIRTUAL_SUSPEND, 0); + return; + }/*end if*/ + + slaveconn.suspend(); +} + +void Slave::resume() +{ + if (d->derived) { // TODO: clean up before KDE 4 + virtual_hook(VIRTUAL_RESUME, 0); + return; + }/*end if*/ + + slaveconn.resume(); +} + +bool Slave::suspended() +{ + if (d->derived) { // TODO: clean up before KDE 4 + SuspendedParams params; + virtual_hook(VIRTUAL_SUSPENDED, ¶ms); + return params.retval; + }/*end if*/ + + return slaveconn.suspended(); +} + +void Slave::send(int cmd, const TQByteArray &arr) { + if (d->derived) { // TODO: clean up before KDE 4 + SendParams params; + params.cmd = cmd; + params.arr = &arr; + virtual_hook(VIRTUAL_SEND, ¶ms); + return; + }/*end if*/ + + slaveconn.send(cmd, arr); +} + +void Slave::gotInput() +{ + ref(); + if (!dispatch()) + { + slaveconn.close(); + dead = true; + TQString arg = m_protocol; + if (!m_host.isEmpty()) + arg += "://"+m_host; + kdDebug(7002) << "slave died pid = " << m_pid << endl; + // Tell the job about the problem. + emit error(ERR_SLAVE_DIED, arg); + // Tell the scheduler about the problem. + emit slaveDied(this); + } + deref(); + // Here we might be dead!! +} + +void Slave::kill() +{ + dead = true; // OO can be such simple. + kdDebug(7002) << "killing slave pid=" << m_pid << " (" << m_protocol << "://" + << m_host << ")" << endl; + if (m_pid) + { + ::kill(m_pid, SIGTERM); + } +} + +void Slave::setHost( const TQString &host, int port, + const TQString &user, const TQString &passwd) +{ + m_host = host; + m_port = port; + m_user = user; + m_passwd = passwd; + + TQByteArray data; + TQDataStream stream( data, IO_WriteOnly ); + stream << m_host << m_port << m_user << m_passwd; + slaveconn.send( CMD_HOST, data ); +} + +void Slave::resetHost() +{ + m_host = "<reset>"; +} + +void Slave::setConfig(const MetaData &config) +{ + TQByteArray data; + TQDataStream stream( data, IO_WriteOnly ); + stream << config; + slaveconn.send( CMD_CONFIG, data ); +} + +Slave* Slave::createSlave( const TQString &protocol, const KURL& url, int& error, TQString& error_text ) +{ + //kdDebug(7002) << "createSlave '" << protocol << "' for " << url.prettyURL() << endl; + // Firstly take into account all special slaves + if (protocol == "data") + return new DataProtocol(); + + DCOPClient *client = kapp->dcopClient(); + if (!client->isAttached()) + client->attach(); + + TQString prefix = locateLocal("socket", TDEGlobal::instance()->instanceName()); + KTempFile socketfile(prefix, TQString::fromLatin1(".slave-socket")); + if ( socketfile.status() != 0 ) + { + error_text = i18n("Unable to create io-slave: %1").arg(strerror(errno)); + error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS; + return 0; + } + +#ifdef __CYGWIN__ + socketfile.close(); +#endif + +#ifndef Q_WS_WIN + TDEServerSocket *kss = new TDEServerSocket(TQFile::encodeName(socketfile.name()).data()); + + Slave *slave = new Slave(kss, protocol, socketfile.name()); +#else + Slave *slave = 0; +#endif + + // WABA: if the dcopserver is running under another uid we don't ask + // tdelauncher for a slave, because the slave might have that other uid + // as well, which might either be a) undesired or b) make it impossible + // for the slave to connect to the application. + // In such case we start the slave via TDEProcess. + // It's possible to force this by setting the env. variable + // TDE_FORK_SLAVES, Clearcase seems to require this. + static bool bForkSlaves = !TQCString(getenv("TDE_FORK_SLAVES")).isEmpty(); + + if (bForkSlaves || !client->isAttached() || client->isAttachedToForeignServer()) + { + TQString _name = KProtocolInfo::exec(protocol); + if (_name.isEmpty()) + { + error_text = i18n("Unknown protocol '%1'.").arg(protocol); + error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS; + delete slave; + return 0; + } + TQString lib_path = KLibLoader::findLibrary(_name.latin1()); + if (lib_path.isEmpty()) + { + error_text = i18n("Can not find io-slave for protocol '%1'.").arg(protocol); + error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS; + return 0; + } + + TDEProcess proc; + + proc << locate("exe", "tdeioslave") << lib_path << protocol << "" << socketfile.name(); + kdDebug(7002) << "tdeioslave" << ", " << lib_path << ", " << protocol << ", " << TQString::null << ", " << socketfile.name() << endl; + + proc.start(TDEProcess::DontCare); + +#ifndef Q_WS_WIN + slave->setPID(proc.pid()); + TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout())); +#endif + return slave; + } + + + TQByteArray params, reply; + TQCString replyType; + TQDataStream stream(params, IO_WriteOnly); + stream << protocol << url.host() << socketfile.name(); + + TQCString launcher = TDEApplication::launcher(); + if (!client->call(launcher, launcher, "requestSlave(TQString,TQString,TQString)", + params, replyType, reply)) { + error_text = i18n("Cannot talk to tdelauncher"); + error = TDEIO::ERR_SLAVE_DEFINED; + delete slave; + return 0; + } + TQDataStream stream2(reply, IO_ReadOnly); + TQString errorStr; + pid_t pid; + stream2 >> pid >> errorStr; + if (!pid) + { + error_text = i18n("Unable to create io-slave:\ntdelauncher said: %1").arg(errorStr); + error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS; + delete slave; + return 0; + } +#ifndef Q_WS_WIN + slave->setPID(pid); + TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout())); +#endif + return slave; +} + +Slave* Slave::holdSlave( const TQString &protocol, const KURL& url ) +{ + //kdDebug(7002) << "holdSlave '" << protocol << "' for " << url.prettyURL() << endl; + // Firstly take into account all special slaves + if (protocol == "data") + return 0; + + DCOPClient *client = kapp->dcopClient(); + if (!client->isAttached()) + client->attach(); + + TQString prefix = locateLocal("socket", TDEGlobal::instance()->instanceName()); + KTempFile socketfile(prefix, TQString::fromLatin1(".slave-socket")); + if ( socketfile.status() != 0 ) + return 0; + +#ifdef __CYGWIN__ + socketfile.close(); + socketfile.unlink(); +#endif + +#ifndef Q_WS_WIN + TDEServerSocket *kss = new TDEServerSocket(TQFile::encodeName(socketfile.name()).data()); + + Slave *slave = new Slave(kss, protocol, socketfile.name()); +#else + Slave *slave = 0; +#endif + + TQByteArray params, reply; + TQCString replyType; + TQDataStream stream(params, IO_WriteOnly); + stream << url << socketfile.name(); + + TQCString launcher = TDEApplication::launcher(); + if (!client->call(launcher, launcher, "requestHoldSlave(KURL,TQString)", + params, replyType, reply)) { + delete slave; + return 0; + } + TQDataStream stream2(reply, IO_ReadOnly); + pid_t pid; + stream2 >> pid; + if (!pid) + { + delete slave; + return 0; + } +#ifndef Q_WS_WIN + slave->setPID(pid); + TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout())); +#endif + return slave; +} + +void Slave::virtual_hook( int id, void* data ) { + TDEIO::SlaveInterface::virtual_hook( id, data ); +} + +#include "slave.moc" |