@@ -144,13 +144,6 @@ namespace network { | |||
// This is only necessary if the Socket has been put in non-blocking | |||
// mode and needs this callback to flush the buffer. | |||
virtual void processSocketWriteEvent(network::Socket* sock) = 0; | |||
// checkTimeouts() allows the server to check socket timeouts, etc. The | |||
// return value is the number of milliseconds to wait before | |||
// checkTimeouts() should be called again. If this number is zero then | |||
// there is no timeout and checkTimeouts() should be called the next time | |||
// an event occurs. | |||
virtual int checkTimeouts() = 0; | |||
}; | |||
} |
@@ -75,6 +75,12 @@ namespace rfb { | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName) = 0; | |||
// terminate() is called by the server when it wishes to terminate | |||
// itself, e.g. because it was configured to terminate when no one is | |||
// using it. | |||
virtual void terminate() = 0; | |||
// setScreenLayout() requests to reconfigure the framebuffer and/or | |||
// the layout of screens. | |||
virtual unsigned int setScreenLayout(int __unused_attr fb_width, |
@@ -51,15 +51,23 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | |||
fenceDataLen(0), fenceData(NULL), congestionTimer(this), | |||
losslessTimer(this), server(server_), updates(false), | |||
updateRenderedCursor(false), removeRenderedCursor(false), | |||
continuousUpdates(false), encodeManager(this), pointerEventTime(0), | |||
clientHasCursor(false) | |||
continuousUpdates(false), encodeManager(this), idleTimer(this), | |||
pointerEventTime(0), clientHasCursor(false) | |||
{ | |||
setStreams(&sock->inStream(), &sock->outStream()); | |||
peerEndpoint.buf = sock->getPeerEndpoint(); | |||
// Configure the socket | |||
setSocketTimeouts(); | |||
lastEventTime = time(0); | |||
// Kick off the idle timer | |||
if (rfb::Server::idleTimeout) { | |||
// minimum of 15 seconds while authenticating | |||
if (rfb::Server::idleTimeout < 15) | |||
idleTimer.start(secsToMillis(15)); | |||
else | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
} | |||
} | |||
@@ -312,36 +320,6 @@ void VNCSConnectionST::setLEDStateOrClose(unsigned int state) | |||
} | |||
int VNCSConnectionST::checkIdleTimeout() | |||
{ | |||
int idleTimeout = rfb::Server::idleTimeout; | |||
if (idleTimeout == 0) return 0; | |||
if (state() != RFBSTATE_NORMAL && idleTimeout < 15) | |||
idleTimeout = 15; // minimum of 15 seconds while authenticating | |||
time_t now = time(0); | |||
if (now < lastEventTime) { | |||
// Someone must have set the time backwards. Set lastEventTime so that the | |||
// idleTimeout will count from now. | |||
vlog.info("Time has gone backwards - resetting idle timeout"); | |||
lastEventTime = now; | |||
} | |||
int timeLeft = lastEventTime + idleTimeout - now; | |||
if (timeLeft < -60) { | |||
// Our callback is over a minute late - someone must have set the time | |||
// forwards. Set lastEventTime so that the idleTimeout will count from | |||
// now. | |||
vlog.info("Time has gone forwards - resetting idle timeout"); | |||
lastEventTime = now; | |||
return secsToMillis(idleTimeout); | |||
} | |||
if (timeLeft <= 0) { | |||
close("Idle timeout"); | |||
return 0; | |||
} | |||
return secsToMillis(timeLeft); | |||
} | |||
bool VNCSConnectionST::getComparerState() | |||
{ | |||
// We interpret a low compression level as an indication that the client | |||
@@ -412,7 +390,8 @@ void VNCSConnectionST::approveConnectionOrClose(bool accept, | |||
void VNCSConnectionST::authSuccess() | |||
{ | |||
lastEventTime = time(0); | |||
if (rfb::Server::idleTimeout) | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
// - Set the connection parameters appropriately | |||
cp.width = server->getPixelBuffer()->width(); | |||
@@ -438,7 +417,8 @@ void VNCSConnectionST::queryConnection(const char* userName) | |||
void VNCSConnectionST::clientInit(bool shared) | |||
{ | |||
lastEventTime = time(0); | |||
if (rfb::Server::idleTimeout) | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
if (rfb::Server::alwaysShared || reverseConnection) shared = true; | |||
if (!accessCheck(AccessNonShared)) shared = true; | |||
if (rfb::Server::neverShared) shared = false; | |||
@@ -457,7 +437,9 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) | |||
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask) | |||
{ | |||
pointerEventTime = lastEventTime = time(0); | |||
if (rfb::Server::idleTimeout) | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
pointerEventTime = time(0); | |||
if (!accessCheck(AccessPtrEvents)) return; | |||
if (!rfb::Server::acceptPointerEvents) return; | |||
pointerEventPos = pos; | |||
@@ -489,7 +471,8 @@ public: | |||
void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
rdr::U32 lookup; | |||
lastEventTime = time(0); | |||
if (rfb::Server::idleTimeout) | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
if (!accessCheck(AccessKeyEvents)) return; | |||
if (!rfb::Server::acceptKeyEvents) return; | |||
@@ -765,6 +748,9 @@ bool VNCSConnectionST::handleTimeout(Timer* t) | |||
close(e.str()); | |||
} | |||
if (t == &idleTimer) | |||
close("Idle timeout"); | |||
return false; | |||
} | |||
@@ -1106,7 +1092,6 @@ void VNCSConnectionST::setLEDState(unsigned int ledstate) | |||
void VNCSConnectionST::setSocketTimeouts() | |||
{ | |||
int timeoutms = rfb::Server::clientWaitTimeMillis; | |||
soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout)); | |||
if (timeoutms == 0) | |||
timeoutms = -1; | |||
sock->inStream().setTimeout(timeoutms); |
@@ -77,11 +77,6 @@ namespace rfb { | |||
void setLEDStateOrClose(unsigned int state); | |||
void approveConnectionOrClose(bool accept, const char* reason); | |||
// checkIdleTimeout() returns the number of milliseconds left until the | |||
// idle timeout expires. If it has expired, the connection is closed and | |||
// zero is returned. Zero is also returned if there is no idle timeout. | |||
int checkIdleTimeout(); | |||
// The following methods never throw exceptions | |||
// getComparerState() returns if this client would like the framebuffer | |||
@@ -182,7 +177,8 @@ namespace rfb { | |||
std::map<rdr::U32, rdr::U32> pressedKeys; | |||
time_t lastEventTime; | |||
Timer idleTimer; | |||
time_t pointerEventTime; | |||
Point pointerEventPos; | |||
bool clientHasCursor; |
@@ -81,10 +81,16 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) | |||
cursor(new Cursor(0, 0, Point(), NULL)), | |||
renderedCursorInvalid(false), | |||
keyRemapper(&KeyRemapper::defInstance), | |||
lastConnectionTime(0), frameTimer(this) | |||
idleTimer(this), disconnectTimer(this), connectTimer(this), | |||
frameTimer(this) | |||
{ | |||
lastUserInputTime = lastDisconnectTime = time(0); | |||
slog.debug("creating single-threaded server %s", name.buf); | |||
// FIXME: Do we really want to kick off these right away? | |||
if (rfb::Server::maxIdleTime) | |||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||
if (rfb::Server::maxDisconnectionTime) | |||
disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); | |||
} | |||
VNCServerST::~VNCServerST() | |||
@@ -144,9 +150,10 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) | |||
name.buf = sock->getPeerEndpoint(); | |||
connectionsLog.status("accepted: %s", name.buf); | |||
if (clients.empty()) { | |||
lastConnectionTime = time(0); | |||
} | |||
// Adjust the exit timers | |||
if (rfb::Server::maxConnectionTime && clients.empty()) | |||
connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); | |||
disconnectTimer.stop(); | |||
VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); | |||
clients.push_front(client); | |||
@@ -164,8 +171,10 @@ void VNCServerST::removeSocket(network::Socket* sock) { | |||
if (pointerClient == *ci) | |||
pointerClient = NULL; | |||
if ((*ci)->authenticated()) | |||
lastDisconnectTime = time(0); | |||
// Adjust the exit timers | |||
connectTimer.stop(); | |||
if (rfb::Server::maxDisconnectionTime && clients.empty()) | |||
disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); | |||
// - Delete the per-Socket resources | |||
delete *ci; | |||
@@ -215,89 +224,6 @@ void VNCServerST::processSocketWriteEvent(network::Socket* sock) | |||
throw rdr::Exception("invalid Socket in VNCServerST"); | |||
} | |||
int VNCServerST::checkTimeouts() | |||
{ | |||
int timeout = 0; | |||
std::list<VNCSConnectionST*>::iterator ci, ci_next; | |||
soonestTimeout(&timeout, Timer::checkTimeouts()); | |||
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { | |||
ci_next = ci; ci_next++; | |||
soonestTimeout(&timeout, (*ci)->checkIdleTimeout()); | |||
} | |||
int timeLeft; | |||
time_t now = time(0); | |||
// Check MaxDisconnectionTime | |||
if (rfb::Server::maxDisconnectionTime && clients.empty()) { | |||
if (now < lastDisconnectTime) { | |||
// Someone must have set the time backwards. | |||
slog.info("Time has gone backwards - resetting lastDisconnectTime"); | |||
lastDisconnectTime = now; | |||
} | |||
timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now; | |||
if (timeLeft < -60) { | |||
// Someone must have set the time forwards. | |||
slog.info("Time has gone forwards - resetting lastDisconnectTime"); | |||
lastDisconnectTime = now; | |||
timeLeft = rfb::Server::maxDisconnectionTime; | |||
} | |||
if (timeLeft <= 0) { | |||
slog.info("MaxDisconnectionTime reached, exiting"); | |||
exit(0); | |||
} | |||
soonestTimeout(&timeout, timeLeft * 1000); | |||
} | |||
// Check MaxConnectionTime | |||
if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) { | |||
if (now < lastConnectionTime) { | |||
// Someone must have set the time backwards. | |||
slog.info("Time has gone backwards - resetting lastConnectionTime"); | |||
lastConnectionTime = now; | |||
} | |||
timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now; | |||
if (timeLeft < -60) { | |||
// Someone must have set the time forwards. | |||
slog.info("Time has gone forwards - resetting lastConnectionTime"); | |||
lastConnectionTime = now; | |||
timeLeft = rfb::Server::maxConnectionTime; | |||
} | |||
if (timeLeft <= 0) { | |||
slog.info("MaxConnectionTime reached, exiting"); | |||
exit(0); | |||
} | |||
soonestTimeout(&timeout, timeLeft * 1000); | |||
} | |||
// Check MaxIdleTime | |||
if (rfb::Server::maxIdleTime) { | |||
if (now < lastUserInputTime) { | |||
// Someone must have set the time backwards. | |||
slog.info("Time has gone backwards - resetting lastUserInputTime"); | |||
lastUserInputTime = now; | |||
} | |||
timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now; | |||
if (timeLeft < -60) { | |||
// Someone must have set the time forwards. | |||
slog.info("Time has gone forwards - resetting lastUserInputTime"); | |||
lastUserInputTime = now; | |||
timeLeft = rfb::Server::maxIdleTime; | |||
} | |||
if (timeLeft <= 0) { | |||
slog.info("MaxIdleTime reached, exiting"); | |||
exit(0); | |||
} | |||
soonestTimeout(&timeout, timeLeft * 1000); | |||
} | |||
return timeout; | |||
} | |||
// VNCServer methods | |||
void VNCServerST::blockUpdates() | |||
@@ -495,7 +421,8 @@ void VNCServerST::setLEDState(unsigned int state) | |||
void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
{ | |||
lastUserInputTime = time(0); | |||
if (rfb::Server::maxIdleTime) | |||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||
// Remap the key if required | |||
if (keyRemapper) { | |||
@@ -513,7 +440,8 @@ void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
void VNCServerST::pointerEvent(VNCSConnectionST* client, | |||
const Point& pos, int buttonMask) | |||
{ | |||
lastUserInputTime = time(0); | |||
if (rfb::Server::maxIdleTime) | |||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||
// Let one client own the cursor whilst buttons are pressed in order | |||
// to provide a bit more sane user experience | |||
@@ -628,6 +556,15 @@ bool VNCServerST::handleTimeout(Timer* t) | |||
} | |||
return true; | |||
} else if (t == &idleTimer) { | |||
slog.info("MaxIdleTime reached, exiting"); | |||
desktop->terminate(); | |||
} else if (t == &disconnectTimer) { | |||
slog.info("MaxDisconnectionTime reached, exiting"); | |||
desktop->terminate(); | |||
} else if (t == &connectTimer) { | |||
slog.info("MaxConnectionTime reached, exiting"); | |||
desktop->terminate(); | |||
} | |||
return false; |
@@ -76,12 +76,6 @@ namespace rfb { | |||
// Flush pending data from the Socket on to the network. | |||
virtual void processSocketWriteEvent(network::Socket* sock); | |||
// checkTimeouts | |||
// Returns the number of milliseconds left until the next idle timeout | |||
// expires. If any have already expired, the corresponding connections | |||
// are closed. Zero is returned if there is no idle timeout. | |||
virtual int checkTimeouts(); | |||
// Methods overridden from VNCServer | |||
@@ -198,9 +192,9 @@ namespace rfb { | |||
KeyRemapper* keyRemapper; | |||
time_t lastUserInputTime; | |||
time_t lastDisconnectTime; | |||
time_t lastConnectionTime; | |||
Timer idleTimer; | |||
Timer disconnectTimer; | |||
Timer connectTimer; | |||
Timer frameTimer; | |||
}; |
@@ -19,6 +19,8 @@ | |||
*/ | |||
#include <assert.h> | |||
#include <signal.h> | |||
#include <unistd.h> | |||
#include <rfb/LogWriter.h> | |||
@@ -273,6 +275,10 @@ void XDesktop::stop() { | |||
pb = 0; | |||
} | |||
void XDesktop::terminate() { | |||
kill(getpid(), SIGTERM); | |||
} | |||
bool XDesktop::isRunning() { | |||
return running; | |||
} |
@@ -49,6 +49,7 @@ public: | |||
// -=- SDesktop interface | |||
virtual void start(rfb::VNCServer* vs); | |||
virtual void stop(); | |||
virtual void terminate(); | |||
bool isRunning(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); |
@@ -322,7 +322,7 @@ int main(int argc, char** argv) | |||
} | |||
} | |||
soonestTimeout(&wait_ms, server.checkTimeouts()); | |||
soonestTimeout(&wait_ms, Timer::checkTimeouts()); | |||
tv.tv_sec = wait_ms / 1000; | |||
tv.tv_usec = (wait_ms % 1000) * 1000; | |||
@@ -357,7 +357,7 @@ int main(int argc, char** argv) | |||
} | |||
} | |||
server.checkTimeouts(); | |||
Timer::checkTimeouts(); | |||
// Client list could have been changed. | |||
server.getSockets(&sockets); |
@@ -22,6 +22,7 @@ | |||
// | |||
#include <assert.h> | |||
#include <signal.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <strings.h> | |||
@@ -367,7 +368,7 @@ void XserverDesktop::blockHandler(int* timeout) | |||
} | |||
// Trigger timers and check when the next will expire | |||
int nextTimeout = server->checkTimeouts(); | |||
int nextTimeout = Timer::checkTimeouts(); | |||
if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout)) | |||
*timeout = nextTimeout; | |||
} catch (rdr::Exception& e) { | |||
@@ -423,6 +424,11 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept, | |||
// SDesktop callbacks | |||
void XserverDesktop::terminate() | |||
{ | |||
kill(getpid(), SIGTERM); | |||
} | |||
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) | |||
{ | |||
vncPointerMove(pos.x + vncGetScreenX(screenIndex), |
@@ -87,6 +87,7 @@ public: | |||
// rfb::SDesktop callbacks | |||
virtual void start(rfb::VNCServer* vs); | |||
virtual void stop(); | |||
virtual void terminate(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); |
@@ -71,6 +71,7 @@ SDisplay::SDisplay() | |||
statusLocation(0), queryConnectionHandler(0), ledState(0) | |||
{ | |||
updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | |||
terminateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | |||
} | |||
SDisplay::~SDisplay() | |||
@@ -140,6 +141,11 @@ void SDisplay::stop() | |||
if (statusLocation) *statusLocation = false; | |||
} | |||
void SDisplay::terminate() | |||
{ | |||
SetEvent(terminateEvent); | |||
} | |||
void SDisplay::queryConnection(network::Socket* sock, | |||
const char* userName) |
@@ -72,6 +72,7 @@ namespace rfb { | |||
virtual void start(VNCServer* vs); | |||
virtual void stop(); | |||
virtual void terminate(); | |||
virtual void queryConnection(network::Socket* sock, | |||
const char* userName); | |||
virtual void pointerEvent(const Point& pos, int buttonmask); | |||
@@ -89,6 +90,7 @@ namespace rfb { | |||
// -=- EventHandler interface | |||
HANDLE getUpdateEvent() {return updateEvent;} | |||
HANDLE getTerminateEvent() {return terminateEvent;} | |||
virtual void processEvent(HANDLE event); | |||
// -=- Notification of whether or not SDisplay is started | |||
@@ -161,6 +163,8 @@ namespace rfb { | |||
// -=- Event signalled to trigger an update to be flushed | |||
Handle updateEvent; | |||
// -=- Event signalled to terminate the server | |||
Handle terminateEvent; | |||
// -=- Where to write the active/inactive indicator to | |||
bool* statusLocation; |
@@ -21,6 +21,7 @@ | |||
#include <winsock2.h> | |||
#include <list> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/Timer.h> | |||
#include <rfb_win32/SocketManager.h> | |||
using namespace rfb; | |||
@@ -161,7 +162,7 @@ int SocketManager::checkTimeouts() { | |||
std::map<HANDLE,ListenInfo>::iterator i; | |||
for (i=listeners.begin(); i!=listeners.end(); i++) | |||
soonestTimeout(&timeout, i->second.server->checkTimeouts()); | |||
soonestTimeout(&timeout, Timer::checkTimeouts()); | |||
std::list<network::Socket*> shutdownSocks; | |||
std::map<HANDLE,ConnInfo>::iterator j, j_next; |
@@ -76,6 +76,7 @@ VNCServerWin32::VNCServerWin32() | |||
// Register the desktop's event to be handled | |||
sockMgr.addEvent(desktop.getUpdateEvent(), &desktop); | |||
sockMgr.addEvent(desktop.getTerminateEvent(), this); | |||
// Register the queued command event to be handled | |||
sockMgr.addEvent(commandEvent, this); | |||
@@ -335,7 +336,8 @@ void VNCServerWin32::processEvent(HANDLE event_) { | |||
command = NoCommand; | |||
commandSig->signal(); | |||
} | |||
} else if (event_ == sessionEvent.h) { | |||
} else if ((event_ == sessionEvent.h) || | |||
(event_ == desktop.getTerminateEvent())) { | |||
stop(); | |||
} | |||
} |