You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/krfb/kinetd/kinetd.cpp

659 lines
17 KiB

/***************************************************************************
kinetd.cpp
--------------
begin : Mon Feb 11 2002
copyright : (C) 2002 by Tim Jansen
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "kinetd.h"
#include "kinetd.moc"
#include "kinetinterface.h"
#include "kuser.h"
#include "uuid.h"
#include <tqregexp.h>
#include <kservicetype.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <knotifyclient.h>
#include <ksockaddr.h>
#include <kextsock.h>
#include <klocale.h>
#include <kglobal.h>
#include <unistd.h>
#include <fcntl.h>
PortListener::PortListener(KService::Ptr s,
KConfig *config,
KServiceRegistry *srvreg) :
m_port(-1),
m_serviceRegistered(false),
m_socket(0),
m_config(config),
m_srvreg(srvreg),
m_dnssdreg(0)
{
m_dnssdRegistered = false;
m_uuid = createUUID();
loadConfig(s);
if (m_valid && m_enabled)
acquirePort();
}
bool PortListener::acquirePort() {
if (m_socket) {
if ((m_port >= m_portBase) &&
(m_port < (m_portBase + m_autoPortRange)))
return true;
else
delete m_socket;
}
m_port = m_portBase;
m_socket = new KServerSocket(m_port, false);
while (!m_socket->bindAndListen()) {
m_port++;
if (m_port >= (m_portBase+m_autoPortRange)) {
kdDebug() << "Kinetd cannot load service "<<m_serviceName
<<": unable to get port" << endl;
m_port = -1;
delete m_socket;
m_socket = 0;
return false;
}
delete m_socket;
m_socket = new KServerSocket(m_port, false);
}
connect(m_socket, TQT_SIGNAL(accepted(KSocket*)),
TQT_SLOT(accepted(KSocket*)));
bool s = m_registerService;
bool sd =m_dnssdRegister;
setServiceRegistrationEnabledInternal(false);
dnssdRegister(false);
setServiceRegistrationEnabledInternal(s);
dnssdRegister(sd);
return true;
}
void PortListener::freePort() {
m_port = -1;
if (m_socket)
delete m_socket;
m_socket = 0;
setServiceRegistrationEnabledInternal(m_registerService);
dnssdRegister(false);
}
void PortListener::loadConfig(KService::Ptr s) {
m_valid = true;
m_autoPortRange = 0;
m_enabled = true;
m_argument = TQString();
m_multiInstance = false;
TQVariant vid, vport, vautoport, venabled, vargument, vmultiInstance, vurl,
vsattributes, vslifetime, vdname, vdtype, vddata;
m_execPath = s->exec().utf8();
vid = s->property("X-KDE-KINETD-id");
vport = s->property("X-KDE-KINETD-port");
vautoport = s->property("X-KDE-KINETD-autoPortRange");
venabled = s->property("X-KDE-KINETD-enabled");
vargument = s->property("X-KDE-KINETD-argument");
vmultiInstance = s->property("X-KDE-KINETD-multiInstance");
vurl = s->property("X-KDE-KINETD-serviceURL");
vsattributes = s->property("X-KDE-KINETD-serviceAttributes");
vslifetime = s->property("X-KDE-KINETD-serviceLifetime");
vdname = s->property("X-KDE-KINETD-DNSSD-Name");
vdtype = s->property("X-KDE-KINETD-DNSSD-Type");
vddata = s->property("X-KDE-KINETD-DNSSD-Properties");
if (!vid.isValid()) {
kdDebug() << "Kinetd cannot load service "<<m_serviceName
<<": no id set" << endl;
m_valid = false;
return;
}
if (!vport.isValid()) {
kdDebug() << "Kinetd cannot load service "<<m_serviceName
<<": invalid port" << endl;
m_valid = false;
return;
}
m_serviceName = vid.toString();
m_serviceLifetime = vslifetime.toInt();
if (m_serviceLifetime < 120) // never less than 120 s
m_serviceLifetime = 120;
m_portBase = vport.toInt();
if (vautoport.isValid())
m_autoPortRange = vautoport.toInt();
if (venabled.isValid())
m_enabled = venabled.toBool();
if (vargument.isValid())
m_argument = vargument.toString();
if (vmultiInstance.isValid())
m_multiInstance = vmultiInstance.toBool();
if (vurl.isValid()) {
m_serviceURL = vurl.toString();
m_registerService = true;
}
else {
m_serviceURL = TQString();
m_registerService = false;
}
if (vsattributes.isValid()) {
m_serviceAttributes = vsattributes.toString();
}
else
m_serviceAttributes = "";
if (vddata.isValid()) {
TQStringList attrs = vddata.toStringList();
for (TQStringList::iterator it=attrs.begin();
it!=attrs.end();it++) {
TQString key = (*it).section('=',0,0);
TQString value = processServiceTemplate((*it).section('=',1))[0];
if (!key.isEmpty()) m_dnssdData[key]=value;
}
}
if (vdname.isValid() && vdtype.isValid()) {
m_dnssdName = processServiceTemplate(vdname.toString())[0];
m_dnssdType = vdtype.toString();
m_dnssdRegister = true;
kdDebug() << "DNS-SD register is enabled\n";
}
else
m_dnssdRegister = false;
m_slpLifetimeEnd = TQDateTime::currentDateTime().addSecs(m_serviceLifetime);
m_defaultPortBase = m_portBase;
m_defaultAutoPortRange = m_autoPortRange;
m_config->setGroup("ListenerConfig");
m_enabled = m_config->readBoolEntry("enabled_" + m_serviceName,
m_enabled);
m_portBase = m_config->readNumEntry("port_base_" + m_serviceName,
m_portBase);
m_autoPortRange = m_config->readNumEntry("auto_port_range_" + m_serviceName,
m_autoPortRange);
TQDateTime nullTime;
m_expirationTime = m_config->readDateTimeEntry("enabled_expiration_"+m_serviceName,
&nullTime);
if ((!m_expirationTime.isNull()) && (m_expirationTime < TQDateTime::currentDateTime()))
m_enabled = false;
m_registerService = m_config->readBoolEntry("enabled_srvreg_"+m_serviceName,
m_registerService);
}
void PortListener::accepted(KSocket *sock) {
TQString host, port;
KSocketAddress *ksa = KExtendedSocket::peerAddress(sock->socket());
if ((!ksa) || !ksa->address()) {
delete sock;
return;
}
KExtendedSocket::resolve(ksa, host, port);
KNotifyClient::event("IncomingConnection",
i18n("Connection from %1").arg(host));
delete ksa;
if ((!m_enabled) ||
((!m_multiInstance) && m_process.isRunning())) {
delete sock;
return;
}
// disable CLOEXEC flag, fixes #77412
fcntl(sock->socket(), F_SETFD, fcntl(sock->socket(), F_GETFD) & ~FD_CLOEXEC);
m_process.clearArguments();
m_process << m_execPath << m_argument << TQString::number(sock->socket());
if (!m_process.start(KProcess::DontCare)) {
KNotifyClient::event("ProcessFailed",
i18n("Call \"%1 %2 %3\" failed").arg(m_execPath)
.arg(m_argument)
.arg(sock->socket()));
}
delete sock;
}
bool PortListener::isValid() {
return m_valid;
}
bool PortListener::isEnabled() {
return m_enabled && m_valid;
}
int PortListener::port() {
return m_port;
}
TQStringList PortListener::processServiceTemplate(const TQString &a) {
TQStringList l;
TQValueVector<KInetInterface> v = KInetInterface::getAllInterfaces(false);
TQValueVector<KInetInterface>::Iterator it = v.begin();
while (it != v.end()) {
KInetSocketAddress *address = (*(it++)).address();
if (!address)
continue;
TQString hostName = address->nodeName();
KUser u;
TQString x = a; // replace does not work in const TQString. Why??
l.append(x.replace(TQRegExp("%h"), KServiceRegistry::encodeAttributeValue(hostName))
.replace(TQRegExp("%p"), TQString::number(m_port))
.replace(TQRegExp("%u"), KServiceRegistry::encodeAttributeValue(u.loginName()))
.replace(TQRegExp("%i"), KServiceRegistry::encodeAttributeValue(m_uuid))
.replace(TQRegExp("%f"), KServiceRegistry::encodeAttributeValue(u.fullName())));
}
return l;
}
bool PortListener::setPort(int port, int autoPortRange) {
if ((port == m_portBase) && (autoPortRange == m_autoPortRange))
return (m_port != -1);
m_config->setGroup("ListenerConfig");
if (port > 0) {
m_portBase = port;
m_autoPortRange = autoPortRange;
m_config->writeEntry("port_base_" + m_serviceName, m_portBase);
m_config->writeEntry("auto_port_range_"+m_serviceName, m_autoPortRange);
}
else {
m_portBase = m_defaultPortBase;
m_autoPortRange = m_defaultAutoPortRange;
m_config->deleteEntry("port_base_" + m_serviceName);
m_config->deleteEntry("auto_port_range_"+m_serviceName);
}
m_config->sync();
if (m_enabled)
return acquirePort();
else
return false;
}
void PortListener::setEnabled(bool e) {
setEnabledInternal(e, TQDateTime());
}
void PortListener::setEnabledInternal(bool e, const TQDateTime &ex) {
m_config->setGroup("ListenerConfig");
m_config->writeEntry("enabled_" + m_serviceName, e);
m_config->writeEntry("enabled_expiration_"+m_serviceName, ex);
m_config->sync();
m_expirationTime = ex;
if (e) {
if (m_port < 0)
acquirePort();
m_enabled = m_port >= 0;
}
else {
freePort();
m_enabled = false;
}
}
void PortListener::setEnabled(const TQDateTime &ex) {
setEnabledInternal(true, ex);
}
bool PortListener::isServiceRegistrationEnabled() {
return m_registerService;
}
void PortListener::setServiceRegistrationEnabled(bool e) {
setServiceRegistrationEnabledInternal(e);
dnssdRegister(e && m_enabled);
m_config->setGroup("ListenerConfig");
m_config->writeEntry("enable_srvreg_" + m_serviceName, e);
m_config->sync();
}
void PortListener::setServiceRegistrationEnabledInternal(bool e) {
m_registerService = e;
if ((!m_srvreg) || m_serviceURL.isNull())
return;
if (m_serviceRegistered == (m_enabled && e))
return;
if (m_enabled && e) {
m_registeredServiceURLs = processServiceTemplate(m_serviceURL);
TQStringList attributes = processServiceTemplate(m_serviceAttributes);
TQStringList::Iterator it = m_registeredServiceURLs.begin();
TQStringList::Iterator it2 = attributes.begin();
while ((it != m_registeredServiceURLs.end()) &&
(it2 != attributes.end())) {
if (!m_srvreg->registerService(
*(it++),
*(it2++),
m_serviceLifetime))
kdDebug(7021) << "Failure registering SLP service (no slpd running?)"<< endl;
}
m_serviceRegistered = true;
// make lifetime 30s shorter, because the timeout is not precise
m_slpLifetimeEnd = TQDateTime::currentDateTime().addSecs(m_serviceLifetime-30);
} else {
TQStringList::Iterator it = m_registeredServiceURLs.begin();
while (it != m_registeredServiceURLs.end())
m_srvreg->unregisterService(*(it++));
m_serviceRegistered = false;
}
}
void PortListener::dnssdRegister(bool e) {
if (m_dnssdName.isNull() || m_dnssdType.isNull())
return;
if (m_dnssdRegistered == e)
return;
if (e) {
m_dnssdRegistered=true;
m_dnssdreg = new DNSSD::PublicService(m_dnssdName,m_dnssdType,m_port);
m_dnssdreg->setTextData(m_dnssdData);
m_dnssdreg->publishAsync();
} else {
m_dnssdRegistered=false;
delete m_dnssdreg;
m_dnssdreg=0;
}
}
void PortListener::refreshRegistration() {
if (m_serviceRegistered && (m_slpLifetimeEnd.addSecs(-90) < TQDateTime::currentDateTime())) {
setServiceRegistrationEnabledInternal(false);
setServiceRegistrationEnabledInternal(true);
}
}
TQDateTime PortListener::expiration() {
return m_expirationTime;
}
TQDateTime PortListener::serviceLifetimeEnd() {
if (m_serviceRegistered)
return m_slpLifetimeEnd;
else
return TQDateTime();
}
TQString PortListener::name() {
return m_serviceName;
}
PortListener::~PortListener() {
setServiceRegistrationEnabledInternal(false);
delete m_socket;
}
KInetD::KInetD(TQCString &n) :
KDEDModule(n)
{
m_config = new KConfig("kinetdrc");
m_srvreg = new KServiceRegistry();
if (!m_srvreg->available()) {
kdDebug(7021) << "SLP not available"<< endl;
delete m_srvreg;
m_srvreg = 0;
}
m_portListeners.setAutoDelete(true);
connect(&m_expirationTimer, TQT_SIGNAL(timeout()), TQT_SLOT(setExpirationTimer()));
connect(&m_portRetryTimer, TQT_SIGNAL(timeout()), TQT_SLOT(portRetryTimer()));
connect(&m_reregistrationTimer, TQT_SIGNAL(timeout()), TQT_SLOT(reregistrationTimer()));
loadServiceList();
}
void KInetD::loadServiceList()
{
m_portListeners.clear();
KService::List kinetdModules =
KServiceType::offers("KInetDModule");
for(KService::List::ConstIterator it = kinetdModules.begin();
it != kinetdModules.end();
it++) {
KService::Ptr s = *it;
PortListener *pl = new PortListener(s, m_config, m_srvreg);
if (pl->isValid())
m_portListeners.append(pl);
else
delete pl;
}
setExpirationTimer();
setPortRetryTimer(true);
setReregistrationTimer();
}
void KInetD::expirationTimer() {
setExpirationTimer();
setReregistrationTimer();
}
void KInetD::setExpirationTimer() {
TQDateTime nextEx = getNextExpirationTime(); // disables expired portlistener!
if (!nextEx.isNull())
m_expirationTimer.start(TQDateTime::currentDateTime().secsTo(nextEx)*1000 + 30000,
false);
else
m_expirationTimer.stop();
}
void KInetD::portRetryTimer() {
setPortRetryTimer(true);
setReregistrationTimer();
}
void KInetD::setReregistrationTimer() {
TQDateTime d;
PortListener *pl = m_portListeners.first();
while (pl) {
TQDateTime d2 = pl->serviceLifetimeEnd();
if (!d2.isNull()) {
if (d2 < TQDateTime::currentDateTime()) {
m_reregistrationTimer.start(0, true);
return;
}
else if (d.isNull() || (d2 < d))
d = d2;
}
pl = m_portListeners.next();
}
if (!d.isNull()) {
int s = TQDateTime::currentDateTime().secsTo(d);
if (s < 30)
s = 30; // max frequency 30s
m_reregistrationTimer.start(s*1000, true);
}
else
m_reregistrationTimer.stop();
}
void KInetD::reregistrationTimer() {
PortListener *pl = m_portListeners.first();
while (pl) {
pl->refreshRegistration();
pl = m_portListeners.next();
}
setReregistrationTimer();
}
void KInetD::setPortRetryTimer(bool retry) {
int unmappedPorts = 0;
PortListener *pl = m_portListeners.first();
while (pl) {
if (pl->isEnabled() && (pl->port() < 0))
if (retry) {
if (!pl->acquirePort())
unmappedPorts++;
}
else if (pl->port() < 0)
unmappedPorts++;
pl = m_portListeners.next();
}
if (unmappedPorts > 0)
m_portRetryTimer.start(30000, false);
else
m_portRetryTimer.stop();
}
PortListener *KInetD::getListenerByName(TQString name)
{
PortListener *pl = m_portListeners.first();
while (pl) {
if (pl->name() == name)
return pl;
pl = m_portListeners.next();
}
return pl;
}
// gets next expiration timer, SIDEEFFECT: disables expired portlisteners while doing this
TQDateTime KInetD::getNextExpirationTime()
{
PortListener *pl = m_portListeners.first();
TQDateTime d;
while (pl) {
TQDateTime d2 = pl->expiration();
if (!d2.isNull()) {
if (d2 < TQDateTime::currentDateTime())
pl->setEnabled(false);
else if (d.isNull() || (d2 < d))
d = d2;
}
pl = m_portListeners.next();
}
return d;
}
TQStringList KInetD::services()
{
TQStringList list;
PortListener *pl = m_portListeners.first();
while (pl) {
list.append(pl->name());
pl = m_portListeners.next();
}
return list;
}
bool KInetD::isEnabled(TQString service)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return false;
return pl->isEnabled();
}
int KInetD::port(TQString service)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return -1;
return pl->port();
}
bool KInetD::setPort(TQString service, int port, int autoPortRange)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return false;
bool s = pl->setPort(port, autoPortRange);
setPortRetryTimer(false);
setReregistrationTimer();
return s;
}
bool KInetD::isInstalled(TQString service)
{
PortListener *pl = getListenerByName(service);
return (pl != 0);
}
void KInetD::setEnabled(TQString service, bool enable)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return;
pl->setEnabled(enable);
setExpirationTimer();
setReregistrationTimer();
}
void KInetD::setEnabled(TQString service, TQDateTime expiration)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return;
pl->setEnabled(expiration);
setExpirationTimer();
setReregistrationTimer();
}
void KInetD::setServiceRegistrationEnabled(TQString service, bool enable)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return;
pl->setServiceRegistrationEnabled(enable);
setReregistrationTimer();
}
bool KInetD::isServiceRegistrationEnabled(TQString service)
{
PortListener *pl = getListenerByName(service);
if (!pl)
return false;
return pl->isServiceRegistrationEnabled();
}
KInetD::~KInetD() {
m_portListeners.clear();
delete m_config;
if (m_srvreg)
delete m_srvreg;
}
extern "C" {
KDE_EXPORT KDEDModule *create_kinetd(TQCString &name)
{
KGlobal::locale()->insertCatalogue("kinetd");
return new KInetD(name);
}
}