* Full author contact details are available in file CREDITS.
*/
+#include <config.h>
+
#include "lyxsocket.h"
#include "debug.h"
#include "frontends/lyx_gui.h"
+#include "support/environment.h"
#include "support/lyxlib.h"
#include "support/socktools.h"
-#include <iostream>
+#include <boost/bind.hpp>
+
#include <cerrno>
+#if defined (_WIN32)
+# include <io.h>
+#endif
+
+using boost::shared_ptr;
using std::auto_ptr;
using std::endl;
// that can connect at the same time.
LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
: func(f),
- fd_(lyx::support::socktools::listen(addr, MAX_CLIENTS)),
+ fd_(lyx::support::socktools::listen(addr, 3)),
address_(addr)
{
if (fd_ == -1) {
// These env vars are used by DVI inverse search
// Needed by xdvi
- lyx::support::putenv("XEDITOR", "lyxclient -g %f %l");
+ lyx::support::setEnv("XEDITOR", "lyxclient -g %f %l");
// Needed by lyxclient
- lyx::support::putenv("LYXSOCKET", address_);
+ lyx::support::setEnv("LYXSOCKET", address_);
+
+ lyx_gui::register_socket_callback(
+ fd_,
+ boost::bind(&LyXServerSocket::serverCallback, this)
+ );
- lyx_gui::set_serversocket_callback(this);
lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
<< fd_ << ' ' << address_ << endl;
}
// Close the socket and remove the address of the filesystem.
LyXServerSocket::~LyXServerSocket()
{
+ lyx_gui::unregister_socket_callback(fd_);
::close(fd_);
lyx::support::unlink(address_);
- while (!clients.empty()) close(*clients.rbegin());
lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
}
-int LyXServerSocket::fd() const
-{
- return fd_;
-}
-
-
string const & LyXServerSocket::address() const
{
return address_;
// is OK and if the number of clients does not exceed MAX_CLIENTS
void LyXServerSocket::serverCallback()
{
- auto_ptr<LyXDataSocket> client(new LyXDataSocket(this));
- if (client->connected()) {
- if (clients.size() == MAX_CLIENTS) {
- client->writeln("BYE:Too many clients connected");
- } else {
- lyx_gui::set_datasocket_callback(client.get());
- clients.insert(client.release());
- return;
- }
+ int const client_fd = lyx::support::socktools::accept(fd_);
+
+ if (fd_ == -1) {
+ lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
+ << endl;
+ return;
+ }
+
+ if (clients.size() >= MAX_CLIENTS) {
+ writeln("BYE:Too many clients connected");
+ return;
}
+
+ // Register the new client.
+ clients[client_fd] =
+ shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
+ lyx_gui::register_socket_callback(
+ client_fd,
+ boost::bind(&LyXServerSocket::dataCallback,
+ this, client_fd)
+ );
}
// Reads and processes input from client and check
// if the connection has been closed
-void LyXServerSocket::dataCallback(LyXDataSocket * client)
+void LyXServerSocket::dataCallback(int fd)
{
+ shared_ptr<LyXDataSocket> client = clients[fd];
+
string line;
string::size_type pos;
bool saidbye = false;
}
if (saidbye || (!client->connected())) {
- close(client);
+ clients.erase(fd);
}
}
-// Removes client callback and deletes client object
-void LyXServerSocket::close(LyXDataSocket * client)
+void LyXServerSocket::writeln(string const & line)
{
- lyx_gui::remove_datasocket_callback(client);
- clients.erase(client);
- delete client;
+ string const linen(line + '\n');
+ int const size = linen.size();
+ int const written = ::write(fd_, linen.c_str(), size);
+ if (written < size) { // Always mean end of connection.
+ if ((written == -1) && (errno == EPIPE)) {
+ // The program will also receive a SIGPIPE
+ // that must be caught
+ lyxerr << "lyx: Server socket " << fd_
+ << " connection closed while writing." << endl;
+ } else {
+ // Anything else, including errno == EAGAIN, must be
+ // considered IO error. EAGAIN should never happen
+ // when line is small
+ lyxerr << "lyx: Server socket " << fd_
+ << " IO error: " << strerror(errno);
+ }
+ }
}
// Debug
// lyxerr << "LyXServerSocket debug dump.\n"
// << "fd = " << fd_ << ", address = " << address_ << ".\n"
// << "Clients: " << clients.size() << ".\n";
-// if (!clients.empty()) {
-// std::set<LyXDataSocket *>::const_iterator client = clients.begin();
-// std::set<LyXDataSocket *>::const_iterator end = clients.end();
-// for (; client != end; ++client)
-// lyxerr << "fd = " << (*client)->fd() << "\n";
-// }
+// std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
+// std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
+// for (; client != end; ++client)
+// lyxerr << "fd = " << client->first << '\n';
// }
-LyXDataSocket::LyXDataSocket(LyXServerSocket * serv)
- :server_(serv),
- fd_(lyx::support::socktools::accept(serv->fd()))
+LyXDataSocket::LyXDataSocket(int fd)
+ : fd_(fd), connected_(true)
{
- if (fd_ == -1) {
- connected_ = false;
- } else {
- lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
- connected_ = true;
- }
+ lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
}
LyXDataSocket::~LyXDataSocket()
{
::close(fd_);
- lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting." << endl;
-}
-
-LyXServerSocket * LyXDataSocket::server() const
-{
- return server_;
-}
-
-
-int LyXDataSocket::fd() const
-{
- return fd_;
+ lyx_gui::unregister_socket_callback(fd_);
+ lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
+ << endl;
}
bool LyXDataSocket::readln(string & line)
{
int const charbuf_size = 100;
- char charbuf[charbuf_size]; // buffer for the ::read() system call
+ char charbuf[charbuf_size]; // buffer for the ::read() system call
int count;
- string::size_type pos;
// read and store characters in buffer
while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
- charbuf[count] = '\0'; // turn it into a c string
- buffer += charbuf;
+ buffer_.append(charbuf, charbuf + count);
}
// Error conditions. The buffer must still be
}
// Cut a line from buffer
- if ((pos = buffer.find('\n')) == string::npos) {
+ string::size_type pos = buffer_.find('\n');
+ if (pos == string::npos) {
lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
<< ": line not completed." << endl;
return false; // No complete line stored
}
- line = buffer.substr(0, pos);
- buffer = buffer.substr(pos + 1);
+ line = buffer_.substr(0, pos);
+ buffer_.erase(0, pos + 1);
return true;
}
// Write a line of the form <key>:<value> to the socket
void LyXDataSocket::writeln(string const & line)
{
- string linen(line + '\n');
- int size = linen.size();
- int written = ::write(fd_, linen.c_str(), size);
- if (written < size) { // Allways mean end of connection.
+ string const linen(line + '\n');
+ int const size = linen.size();
+ int const written = ::write(fd_, linen.c_str(), size);
+ if (written < size) { // Always mean end of connection.
if ((written == -1) && (errno == EPIPE)) {
// The program will also receive a SIGPIPE
// that must be catched