* Full author contact details are available in file CREDITS.
*/
+#include <config.h>
+
#include "lyxsocket.h"
#include "debug.h"
#include "LyXAction.h"
#include "lyxfunc.h"
-#include "frontends/lyx_gui.h"
+#include "frontends/Application.h"
+#include "support/environment.h"
+#include "support/filename.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;
using std::string;
+namespace lyx {
+
// Address is the unix address for the socket.
// MAX_CLIENTS is the maximum number of clients
// 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_(support::socktools::listen(addr, 3)),
address_(addr)
{
if (fd_ == -1) {
lyxerr << "lyx: Disabling LyX socket." << endl;
return;
}
- lyx_gui::set_serversocket_callback(this);
- lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
+
+ // These env vars are used by DVI inverse search
+ // Needed by xdvi
+ support::setEnv("XEDITOR", "lyxclient -g %f %l");
+ // Needed by lyxclient
+ support::setEnv("LYXSOCKET", address_);
+
+ theApp()->registerSocketCallback(
+ fd_,
+ boost::bind(&LyXServerSocket::serverCallback, this)
+ );
+
+ LYXERR(Debug::LYXSERVER) << "lyx: New server socket "
<< fd_ << ' ' << address_ << endl;
}
// Close the socket and remove the address of the filesystem.
LyXServerSocket::~LyXServerSocket()
{
- ::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_;
+ if (fd_ != -1) {
+ BOOST_ASSERT (theApp());
+ theApp()->unregisterSocketCallback(fd_);
+ if (::close(fd_) != 0)
+ lyxerr << "lyx: Server socket " << fd_
+ << " IO error on closing: " << strerror(errno);
+ }
+ support::unlink(support::FileName(address_));
+ LYXERR(Debug::LYXSERVER) << "lyx: Server socket quitting" << endl;
}
// is OK and if the number of clients does not exceed MAX_CLIENTS
void LyXServerSocket::serverCallback()
{
- LyXDataSocket * client = new LyXDataSocket(this);
- if (client->connected()) {
- if (clients.size() == MAX_CLIENTS) {
- client->writeln("BYE:Too many clients connected");
- } else {
- clients.insert(client);
- lyx_gui::set_datasocket_callback(client);
- return;
- }
+ int const client_fd = support::socktools::accept(fd_);
+
+ if (fd_ == -1) {
+ LYXERR(Debug::LYXSERVER) << "lyx: Failed to accept new client"
+ << endl;
+ return;
}
- delete client;
+
+ 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));
+ theApp()->registerSocketCallback(
+ 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 (key == "LYXCMD") {
string const cmd = line.substr(pos + 1);
func->dispatch(lyxaction.lookupFunc(cmd));
- string const rval = func->getMessage();
+ string const rval = to_utf8(func->getMessage());
if (func->errorStat()) {
client->writeln("ERROR:" + cmd + ':' + rval);
} else {
}
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
// void LyXServerSocket::dump() const
// {
-// 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";
-// }
+// lyxerr << "LyXServerSocket debug dump.\n"
+// << "fd = " << fd_ << ", address = " << address_ << ".\n"
+// << "Clients: " << clients.size() << ".\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_;
-}
-
+ if (::close(fd_) != 0)
+ lyxerr << "lyx: Data socket " << fd_
+ << " IO error on closing: " << strerror(errno);
-int LyXDataSocket::fd() const
-{
- return fd_;
+ theApp()->unregisterSocketCallback(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
// processed for lines read
if (count == 0) { // EOF -- connection closed
- lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
+ LYXERR(Debug::LYXSERVER) << "lyx: Data socket " << fd_
<< ": connection closed." << endl;
connected_ = false;
} else if ((count == -1) && (errno != EAGAIN)) { // IO error
}
// Cut a line from buffer
- if ((pos = buffer.find('\n')) == string::npos) {
- lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
+ 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
connected_ = false;
}
}
+
+
+} // namespace lyx