mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
Jo�o Assirati's lyxsocket patch.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@7910 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
a316a54e60
commit
45a8d5a93a
1
development/lyxsocket/.cvsignore
Normal file
1
development/lyxsocket/.cvsignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
lyxclient
|
564
development/lyxsocket/lyxclient.C
Normal file
564
development/lyxsocket/lyxclient.C
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
/**
|
||||||
|
* \file lyxclient.C
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author João Luis M. Assirati
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
// getpid(), getppid()
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// select()
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
// opendir(), closedir(), readdir()
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
// stat()
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
// socket(), connect()
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
// fcntl()
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
|
using std::cin;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
namespace support {
|
||||||
|
|
||||||
|
string itoa(unsigned int i)
|
||||||
|
{
|
||||||
|
string str;
|
||||||
|
if(!i) {
|
||||||
|
str = "0";
|
||||||
|
} else {
|
||||||
|
while((0 < i) && (i <= 9)) {
|
||||||
|
str = static_cast<char>('0' + i % 10) + str;
|
||||||
|
i /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool prefixIs(string const & a, string const & pre)
|
||||||
|
{
|
||||||
|
char const * p_a = a.c_str();
|
||||||
|
char const * p_pre = pre.c_str();
|
||||||
|
while ((*p_a != '\0') && (*p_pre != '\0') && (*p_a == *p_pre)) {
|
||||||
|
++p_a;
|
||||||
|
++p_pre;
|
||||||
|
}
|
||||||
|
if (*p_pre == '\0') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parts stolen from lyx::support::DirList()
|
||||||
|
// Returns pathnames begining with dir and ending with
|
||||||
|
// pathname separator (/ in unix)
|
||||||
|
vector<string> lyxSockets(string const & dir, string const & pid)
|
||||||
|
{
|
||||||
|
vector<string> dirlist;
|
||||||
|
DIR * dirp = ::opendir(dir.c_str());
|
||||||
|
if (!dirp) {
|
||||||
|
cerr << "lyxclient: Could not read dir " << dir
|
||||||
|
<< ": " << strerror(errno);
|
||||||
|
return dirlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirent * dire;
|
||||||
|
while ((dire = ::readdir(dirp))) {
|
||||||
|
string const fil = dire->d_name;
|
||||||
|
if (prefixIs(fil, "lyx_tmpdir" + pid)) {
|
||||||
|
string lyxsocket = dir + '/' + fil + "/lyxsocket";
|
||||||
|
struct stat status;
|
||||||
|
// not checking if it is a socket -- just if it exists
|
||||||
|
if (!::stat(lyxsocket.c_str(), &status)) {
|
||||||
|
dirlist.push_back(lyxsocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::closedir(dirp);
|
||||||
|
return dirlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace socktools {
|
||||||
|
|
||||||
|
int connect(string const & name)
|
||||||
|
{
|
||||||
|
int fd; // File descriptor for the socket
|
||||||
|
sockaddr_un addr; // Structure that hold the socket address
|
||||||
|
|
||||||
|
// char sun_path[108]
|
||||||
|
string::size_type len = name.size();
|
||||||
|
if(len > 107) {
|
||||||
|
cerr << "lyxclient: Socket address '" << name
|
||||||
|
<< "' too long." << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Synonims for AF_UNIX are AF_LOCAL and AF_FILE
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
name.copy(addr.sun_path, 107);
|
||||||
|
addr.sun_path[len] = '\0';
|
||||||
|
|
||||||
|
if((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) {
|
||||||
|
cerr << "lyxclient: Could not create socket: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(::connect(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) {
|
||||||
|
cerr << "lyxclient: Could not connect to socket " << name
|
||||||
|
<< ": " << strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
||||||
|
cerr << "lyxclient: Could not set O_NONBLOCK for socket: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace socktools
|
||||||
|
} // namespace support
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Class IOWatch ------------------------------------------------------------
|
||||||
|
class IOWatch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IOWatch();
|
||||||
|
void clear();
|
||||||
|
void addfd(int);
|
||||||
|
bool wait(double);
|
||||||
|
bool wait();
|
||||||
|
bool isset(int fd);
|
||||||
|
|
||||||
|
private:
|
||||||
|
fd_set des;
|
||||||
|
fd_set act;
|
||||||
|
};
|
||||||
|
|
||||||
|
IOWatch::IOWatch() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
void IOWatch::clear() {
|
||||||
|
FD_ZERO(&des);
|
||||||
|
}
|
||||||
|
void IOWatch::addfd(int fd) {
|
||||||
|
FD_SET(fd, &des);
|
||||||
|
}
|
||||||
|
bool IOWatch::wait(double timeout) {
|
||||||
|
timeval to;
|
||||||
|
to.tv_sec = static_cast<long int>(timeout);
|
||||||
|
to.tv_usec = static_cast<long int>((timeout - to.tv_sec)*1E6);
|
||||||
|
act = des;
|
||||||
|
return select(FD_SETSIZE, &act,
|
||||||
|
(fd_set *)0, (fd_set *)0, &to);
|
||||||
|
}
|
||||||
|
bool IOWatch::wait() {
|
||||||
|
act = des;
|
||||||
|
return select(FD_SETSIZE, &act,
|
||||||
|
(fd_set *)0, (fd_set *)0, (timeval *)0);
|
||||||
|
}
|
||||||
|
bool IOWatch::isset(int fd) {
|
||||||
|
return FD_ISSET(fd, &act);
|
||||||
|
}
|
||||||
|
// ~Class IOWatch ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Class LyXDataSocket -------------------------------------------------------
|
||||||
|
// Modified LyXDataSocket class for use with the client
|
||||||
|
class LyXDataSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LyXDataSocket(string const &);
|
||||||
|
~LyXDataSocket();
|
||||||
|
// File descriptor of the connection
|
||||||
|
int fd() const;
|
||||||
|
// Connection status
|
||||||
|
bool connected() const;
|
||||||
|
// Line buffered input from the socket
|
||||||
|
bool readln(string &);
|
||||||
|
// Write the string + '\n' to the socket
|
||||||
|
void writeln(string const &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// File descriptor for the data socket
|
||||||
|
int fd_;
|
||||||
|
// True if the connection is up
|
||||||
|
bool connected_;
|
||||||
|
// buffer for input data
|
||||||
|
string buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
LyXDataSocket::LyXDataSocket(string const & address)
|
||||||
|
{
|
||||||
|
if ((fd_ = support::socktools::connect(address)) == -1) {
|
||||||
|
connected_ = false;
|
||||||
|
} else {
|
||||||
|
connected_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LyXDataSocket::~LyXDataSocket()
|
||||||
|
{
|
||||||
|
::close(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LyXDataSocket::fd() const
|
||||||
|
{
|
||||||
|
return fd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LyXDataSocket::connected() const
|
||||||
|
{
|
||||||
|
return connected_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if there was a complete line to input
|
||||||
|
// A line is of the form <key>:<value>
|
||||||
|
// A line not of this form will not be passed
|
||||||
|
// The line read is splitted and stored in 'key' and 'value'
|
||||||
|
bool LyXDataSocket::readln(string & line)
|
||||||
|
{
|
||||||
|
int const charbuf_size = 100;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error conditions. The buffer must still be
|
||||||
|
// processed for lines read
|
||||||
|
if (count == 0) { // EOF -- connection closed
|
||||||
|
connected_ = false;
|
||||||
|
} else if ((count == -1) && (errno != EAGAIN)) { // IO error
|
||||||
|
cerr << "lyxclient: IO error." << endl;
|
||||||
|
connected_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut a line from buffer
|
||||||
|
if ((pos = buffer.find('\n')) == string::npos)
|
||||||
|
return false; // No complete line stored
|
||||||
|
line = buffer.substr(0, pos);
|
||||||
|
buffer = buffer.substr(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.
|
||||||
|
if ((written == -1) && (errno == EPIPE)) {
|
||||||
|
// The program will also receive a SIGPIPE
|
||||||
|
// that must be catched
|
||||||
|
cerr << "lyxclient: connection closed while writing."
|
||||||
|
<< endl;
|
||||||
|
} else {
|
||||||
|
// Anything else, including errno == EAGAIN, must be
|
||||||
|
// considered IO error. EAGAIN should never happen
|
||||||
|
// when line is small
|
||||||
|
cerr << "lyxclient: IO error: " << strerror(errno);
|
||||||
|
}
|
||||||
|
connected_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ~Class LyXDataSocket -------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Class CmdLineParser -------------------------------------------------------
|
||||||
|
class CmdLineParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef int (*optfunc)(vector<char *> const & args);
|
||||||
|
std::map<string, optfunc> helper;
|
||||||
|
std::map<string, bool> isset;
|
||||||
|
bool parse(int, char * []);
|
||||||
|
vector<char *> nonopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CmdLineParser::parse(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
int opt = 1;
|
||||||
|
while(opt < argc) {
|
||||||
|
vector<char *> args;
|
||||||
|
if(helper[argv[opt]]) {
|
||||||
|
isset[argv[opt]] = true;
|
||||||
|
int arg = opt + 1;
|
||||||
|
while((arg < argc) && (!helper[argv[arg]])) {
|
||||||
|
args.push_back(argv[arg]);
|
||||||
|
++arg;
|
||||||
|
}
|
||||||
|
int taken = helper[argv[opt]](args);
|
||||||
|
if(taken == -1) return false;
|
||||||
|
opt += 1 + taken;
|
||||||
|
} else {
|
||||||
|
if(argv[opt][0] == '-') {
|
||||||
|
if((argv[opt][1] == '-')
|
||||||
|
&& (argv[opt][2]== '\0')) {
|
||||||
|
++opt;
|
||||||
|
while(opt < argc) {
|
||||||
|
nonopt.push_back(argv[opt]);
|
||||||
|
++opt;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
cerr << "lyxclient: unknown option "
|
||||||
|
<< argv[opt] << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nonopt.push_back(argv[opt]);
|
||||||
|
++opt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ~Class CmdLineParser -------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace cmdline
|
||||||
|
{
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
cerr << "Usage: lyxclient [options]" << endl
|
||||||
|
<< "Options are:" << endl
|
||||||
|
<< " -a address set address of the lyx socket" << endl
|
||||||
|
<< " -t directory set system temporary directory" << endl
|
||||||
|
<< " -p pid select a running lyx by pid" << endl
|
||||||
|
<< " -c command send a single command and quit" << endl
|
||||||
|
<< " -g file row send a command to go to file and row" << endl
|
||||||
|
<< " -n name set client name" << endl
|
||||||
|
<< " -h name display this help end exit" << endl
|
||||||
|
<< "If -a is not used, lyxclient will use the arguments of -t and -p to look for" << endl
|
||||||
|
<< "a running lyx. If -t is not set, 'directory' defaults to /tmp. If -p is set," << endl
|
||||||
|
<< "lyxclient will connect only to a lyx with the specified pid. Options -c and -g" << endl
|
||||||
|
<< "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient" << endl
|
||||||
|
<< "will read commands from standard input and disconnect when command read is BYE:"
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
string clientName(support::itoa(::getppid()) + ">" + support::itoa(::getpid()));
|
||||||
|
int n(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 1) {
|
||||||
|
cerr << "lyxclient: The option -n requires 1 argument."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
clientName = arg[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string singleCommand;
|
||||||
|
int c(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 1) {
|
||||||
|
cerr << "lyxclient: The option -c requires 1 argument."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
singleCommand = arg[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int g(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 2) {
|
||||||
|
cerr << "lyxclient: The option -g requires 2 arguments."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
singleCommand = "LYXCMD:server-goto-file-row "
|
||||||
|
+ static_cast<string>(arg[0]) + ' '
|
||||||
|
+ static_cast<string>(arg[1]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * serverAddress;
|
||||||
|
int a(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 1) {
|
||||||
|
cerr << "lyxclient: The option -a requires 1 argument."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
serverAddress = arg[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string mainTmp("/tmp");
|
||||||
|
int t(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 1) {
|
||||||
|
cerr << "lyxclient: The option -t requires 1 argument."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mainTmp = arg[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string serverPid; // Init to empty string
|
||||||
|
int p(vector<char *> const & arg)
|
||||||
|
{
|
||||||
|
if(arg.size() < 1) {
|
||||||
|
cerr << "lyxclient: The option -p requires 1 argument."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
serverPid = arg[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cmdline
|
||||||
|
|
||||||
|
using support::prefixIs;
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
CmdLineParser parser;
|
||||||
|
parser.helper["-h"] = cmdline::h;
|
||||||
|
parser.helper["-c"] = cmdline::c;
|
||||||
|
parser.helper["-g"] = cmdline::g;
|
||||||
|
parser.helper["-n"] = cmdline::n;
|
||||||
|
parser.helper["-a"] = cmdline::a;
|
||||||
|
parser.helper["-t"] = cmdline::t;
|
||||||
|
parser.helper["-p"] = cmdline::p;
|
||||||
|
// Command line failure conditions:
|
||||||
|
if((!parser.parse(argc, argv))
|
||||||
|
|| (parser.isset["-c"] && parser.isset["-g"])
|
||||||
|
|| (parser.isset["-a"] && parser.isset["-p"])) {
|
||||||
|
cmdline::usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LyXDataSocket * server;
|
||||||
|
|
||||||
|
if(parser.isset["-a"]) {
|
||||||
|
server = new LyXDataSocket(cmdline::serverAddress);
|
||||||
|
if(!server->connected()) {
|
||||||
|
cerr << "lyxclient: " << "Could not connect to "
|
||||||
|
<< cmdline::serverAddress << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We have to look for an address.
|
||||||
|
// serverPid can be empty.
|
||||||
|
vector<string> addrs = support::lyxSockets(cmdline::mainTmp, cmdline::serverPid);
|
||||||
|
vector<string>::iterator addr = addrs.begin();
|
||||||
|
vector<string>::iterator end = addrs.end();
|
||||||
|
while (addr < end) {
|
||||||
|
server = new LyXDataSocket(*addr);
|
||||||
|
if(server->connected()) break;
|
||||||
|
cerr << "lyxclient: " << "Could not connect to "
|
||||||
|
<< *addr << endl;
|
||||||
|
delete server;
|
||||||
|
++addr;
|
||||||
|
}
|
||||||
|
if(addr == end) {
|
||||||
|
cerr << "lyxclient: No suitable server found." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cerr << "lyxclient: " << "Connected to " << *addr << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int const serverfd = server->fd();
|
||||||
|
|
||||||
|
IOWatch iowatch;
|
||||||
|
iowatch.addfd(serverfd);
|
||||||
|
|
||||||
|
// Used to read from server
|
||||||
|
string answer;
|
||||||
|
|
||||||
|
// Send greeting
|
||||||
|
server->writeln("HELLO:" + cmdline::clientName);
|
||||||
|
// wait at most 2 seconds until server responds
|
||||||
|
iowatch.wait(2.0);
|
||||||
|
if(iowatch.isset(serverfd) && server->readln(answer)) {
|
||||||
|
if(prefixIs(answer, "BYE:")) {
|
||||||
|
cerr << "lyxclient: Server disconnected." << endl;
|
||||||
|
cout << answer << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cerr << "lyxclient: No answer from server." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parser.isset["-g"] || parser.isset["-c"]) {
|
||||||
|
server->writeln(cmdline::singleCommand);
|
||||||
|
iowatch.wait(2.0);
|
||||||
|
if(iowatch.isset(serverfd) && server->readln(answer)) {
|
||||||
|
cout << answer;
|
||||||
|
if(prefixIs(answer, "ERROR:")) return 1;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
cerr << "lyxclient: No answer from server." << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take commands from stdin
|
||||||
|
iowatch.addfd(0); // stdin
|
||||||
|
bool saidbye = false;
|
||||||
|
while((!saidbye) && server->connected()) {
|
||||||
|
iowatch.wait();
|
||||||
|
if(iowatch.isset(0)) {
|
||||||
|
string command;
|
||||||
|
cin >> command;
|
||||||
|
if(command == "BYE:") {
|
||||||
|
server->writeln("BYE:");
|
||||||
|
saidbye = true;
|
||||||
|
} else {
|
||||||
|
server->writeln("LYXCMD:" + command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(iowatch.isset(serverfd)) {
|
||||||
|
while(server->readln(answer))
|
||||||
|
cout << answer << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,3 +1,10 @@
|
|||||||
|
2003-10-13 Joao Luis Meloni Assirati <assirati@fma.if.usp.br>
|
||||||
|
|
||||||
|
* lyxsocket.[Ch]: new files. A simple local socket interface for lyx.
|
||||||
|
|
||||||
|
* Makefile.am: add lyxsocket.[Ch].
|
||||||
|
|
||||||
|
* lyx_main.C (error_handler): handle SIGPIPE.
|
||||||
|
|
||||||
2003-10-13 André Pönitz <poenitz@gmx.net>
|
2003-10-13 André Pönitz <poenitz@gmx.net>
|
||||||
|
|
||||||
|
@ -209,6 +209,8 @@ lyx_SOURCES = \
|
|||||||
lyxrow_funcs.h \
|
lyxrow_funcs.h \
|
||||||
lyxserver.C \
|
lyxserver.C \
|
||||||
lyxserver.h \
|
lyxserver.h \
|
||||||
|
lyxsocket.C \
|
||||||
|
lyxsocket.h \
|
||||||
lyxtext.h \
|
lyxtext.h \
|
||||||
lyxtextclass.C \
|
lyxtextclass.C \
|
||||||
lyxtextclass.h \
|
lyxtextclass.h \
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2003-10-13 Joao Luis Meloni Assirati <assirati@fma.if.usp.br>
|
||||||
|
|
||||||
|
* lyx_gui.h (set_datasocket_callback, set_serversocket_callback,
|
||||||
|
remove_datasocket_callback, remove_serversocket_callback):
|
||||||
|
new function declarations, implemented in the various frontends.
|
||||||
|
|
||||||
2003-10-07 Martin Vermeer <martin.vermeer@hut.fi>
|
2003-10-07 Martin Vermeer <martin.vermeer@hut.fi>
|
||||||
|
|
||||||
* lyx_gui.h: add <string> and other small fixes to make Lars'
|
* lyx_gui.h: add <string> and other small fixes to make Lars'
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2003-10-13 Angus Leeming <leeming@lyx.org>
|
||||||
|
|
||||||
|
* lyx_gui.C (set_datasocket_callback, set_serversocket_callback,
|
||||||
|
remove_datasocket_callback, remove_serversocket_callback):
|
||||||
|
placeholder functions, enabling the frontend to be linked.
|
||||||
|
|
||||||
2003-10-08 Angus Leeming <leeming@lyx.org>
|
2003-10-08 Angus Leeming <leeming@lyx.org>
|
||||||
|
|
||||||
Fix doxygen warnings.
|
Fix doxygen warnings.
|
||||||
|
@ -435,6 +435,22 @@ void lyx_gui::remove_read_callback(int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_datasocket_callback(LyXDataSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_datasocket_callback(LyXDataSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void set_serversocket_callback(LyXServerSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_serversocket_callback(LyXServerSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
string const lyx_gui::roman_font_name()
|
string const lyx_gui::roman_font_name()
|
||||||
{
|
{
|
||||||
return "times";
|
return "times";
|
||||||
|
@ -22,6 +22,8 @@ class Dialogs;
|
|||||||
class LColor_color;
|
class LColor_color;
|
||||||
class LyXFont;
|
class LyXFont;
|
||||||
class LyXComm;
|
class LyXComm;
|
||||||
|
class LyXDataSocket;
|
||||||
|
class LyXServerSocket;
|
||||||
class FuncRequest;
|
class FuncRequest;
|
||||||
|
|
||||||
/// GUI interaction
|
/// GUI interaction
|
||||||
@ -94,12 +96,16 @@ bool font_available(LyXFont const & font);
|
|||||||
* add a callback for I/O read notification
|
* add a callback for I/O read notification
|
||||||
*/
|
*/
|
||||||
void set_read_callback(int fd, LyXComm * comm);
|
void set_read_callback(int fd, LyXComm * comm);
|
||||||
|
void set_datasocket_callback(LyXDataSocket *);
|
||||||
|
void set_serversocket_callback(LyXServerSocket *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove a I/O read callback
|
* remove a I/O read callback
|
||||||
* @param fd file descriptor
|
* @param fd file descriptor
|
||||||
*/
|
*/
|
||||||
void remove_read_callback(int fd);
|
void remove_read_callback(int fd);
|
||||||
|
void remove_datasocket_callback(LyXDataSocket *);
|
||||||
|
void remove_serversocket_callback(LyXServerSocket *);
|
||||||
|
|
||||||
} // namespace lyx_gui
|
} // namespace lyx_gui
|
||||||
|
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2003-10-13 Angus Leeming <leeming@lyx.org>
|
||||||
|
|
||||||
|
* lyx_gui.C (set_datasocket_callback, set_serversocket_callback,
|
||||||
|
remove_datasocket_callback, remove_serversocket_callback):
|
||||||
|
placeholder functions, enabling the frontend to be linked.
|
||||||
|
|
||||||
2003-10-13 Lars Gullik Bjønnes <larsbj@gullik.net>
|
2003-10-13 Lars Gullik Bjønnes <larsbj@gullik.net>
|
||||||
|
|
||||||
* lyx_gui.C (start): adjust for distpatch change
|
* lyx_gui.C (start): adjust for distpatch change
|
||||||
|
@ -236,6 +236,22 @@ void remove_read_callback(int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_datasocket_callback(LyXDataSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_datasocket_callback(LyXDataSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void set_serversocket_callback(LyXServerSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_serversocket_callback(LyXServerSocket * /* p */)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
string const roman_font_name()
|
string const roman_font_name()
|
||||||
{
|
{
|
||||||
if (!use_gui)
|
if (!use_gui)
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2003-10-13 Joao Luis Meloni Assirati <assirati@fma.if.usp.br>
|
||||||
|
|
||||||
|
* lyx_gui.C (set_datasocket_callback, set_serversocket_callback,
|
||||||
|
remove_datasocket_callback, remove_serversocket_callback):
|
||||||
|
functions to inform the core when the socket has changed state.
|
||||||
|
|
||||||
2003-10-13 Lars Gullik Bjønnes <larsbj@gullik.net>
|
2003-10-13 Lars Gullik Bjønnes <larsbj@gullik.net>
|
||||||
|
|
||||||
* lyx_gui.C (start): adjust for dispatch change
|
* lyx_gui.C (start): adjust for dispatch change
|
||||||
|
@ -29,12 +29,13 @@
|
|||||||
#include "lyxfunc.h"
|
#include "lyxfunc.h"
|
||||||
#include "lyxrc.h"
|
#include "lyxrc.h"
|
||||||
#include "lyxserver.h"
|
#include "lyxserver.h"
|
||||||
|
#include "lyxsocket.h"
|
||||||
|
|
||||||
#include "graphics/LoaderQueue.h"
|
#include "graphics/LoaderQueue.h"
|
||||||
|
|
||||||
|
#include "support/filetools.h"
|
||||||
#include "support/lyxlib.h"
|
#include "support/lyxlib.h"
|
||||||
#include "support/os.h"
|
#include "support/os.h"
|
||||||
#include "support/filetools.h"
|
|
||||||
#include "support/path_defines.h"
|
#include "support/path_defines.h"
|
||||||
|
|
||||||
#include "lyx_forms.h"
|
#include "lyx_forms.h"
|
||||||
@ -69,6 +70,7 @@ extern BufferList bufferlist;
|
|||||||
|
|
||||||
// FIXME: wrong place !
|
// FIXME: wrong place !
|
||||||
LyXServer * lyxserver;
|
LyXServer * lyxserver;
|
||||||
|
LyXServerSocket * lyxsocket;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -289,6 +291,8 @@ void start(string const & batch, vector<string> const & files)
|
|||||||
// FIXME: some code below needs moving
|
// FIXME: some code below needs moving
|
||||||
|
|
||||||
lyxserver = new LyXServer(&view.getLyXFunc(), lyxrc.lyxpipes);
|
lyxserver = new LyXServer(&view.getLyXFunc(), lyxrc.lyxpipes);
|
||||||
|
lyxsocket = new LyXServerSocket(&view.getLyXFunc(),
|
||||||
|
os::slashify_path(os::getTmpDir() + "/lyxsocket"));
|
||||||
|
|
||||||
vector<string>::const_iterator cit = files.begin();
|
vector<string>::const_iterator cit = files.begin();
|
||||||
vector<string>::const_iterator end = files.end();
|
vector<string>::const_iterator end = files.end();
|
||||||
@ -313,6 +317,7 @@ void start(string const & batch, vector<string> const & files)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: breaks emergencyCleanup
|
// FIXME: breaks emergencyCleanup
|
||||||
|
delete lyxsocket;
|
||||||
delete lyxserver;
|
delete lyxserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,6 +387,20 @@ void C_read_callback(int, void * data)
|
|||||||
comm->read_ready();
|
comm->read_ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void C_datasocket_callback(int, void * data)
|
||||||
|
{
|
||||||
|
LyXDataSocket * client = static_cast<LyXDataSocket *>(data);
|
||||||
|
client->server()->dataCallback(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void C_serversocket_callback(int, void * data)
|
||||||
|
{
|
||||||
|
LyXServerSocket * server = static_cast<LyXServerSocket *>(data);
|
||||||
|
server->serverCallback();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_read_callback(int fd, LyXComm * comm)
|
void set_read_callback(int fd, LyXComm * comm)
|
||||||
@ -389,12 +408,30 @@ void set_read_callback(int fd, LyXComm * comm)
|
|||||||
fl_add_io_callback(fd, FL_READ, C_read_callback, comm);
|
fl_add_io_callback(fd, FL_READ, C_read_callback, comm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void remove_read_callback(int fd)
|
void remove_read_callback(int fd)
|
||||||
{
|
{
|
||||||
fl_remove_io_callback(fd, FL_READ, C_read_callback);
|
fl_remove_io_callback(fd, FL_READ, C_read_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_datasocket_callback(LyXDataSocket * p)
|
||||||
|
{
|
||||||
|
fl_add_io_callback(p->fd(), FL_READ, C_datasocket_callback, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_datasocket_callback(LyXDataSocket * p)
|
||||||
|
{
|
||||||
|
fl_remove_io_callback(p->fd(), FL_READ, C_datasocket_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_serversocket_callback(LyXServerSocket * p)
|
||||||
|
{
|
||||||
|
fl_add_io_callback(p->fd(), FL_READ, C_serversocket_callback, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_serversocket_callback(LyXServerSocket * p)
|
||||||
|
{
|
||||||
|
fl_remove_io_callback(p->fd(), FL_READ, C_serversocket_callback);
|
||||||
|
}
|
||||||
|
|
||||||
string const roman_font_name()
|
string const roman_font_name()
|
||||||
{
|
{
|
||||||
|
@ -214,6 +214,13 @@ static void error_handler(int err_sig)
|
|||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
// no comments
|
// no comments
|
||||||
break;
|
break;
|
||||||
|
case SIGPIPE:
|
||||||
|
// This will be received if lyx tries to write to a socket
|
||||||
|
// whose reading end was closed. It can safely be ignored,
|
||||||
|
// as in this case the ::write() system call will return -1
|
||||||
|
// and errno will be set to EPIPE
|
||||||
|
return;
|
||||||
|
//break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deinstall the signal handlers
|
// Deinstall the signal handlers
|
||||||
@ -222,6 +229,7 @@ static void error_handler(int err_sig)
|
|||||||
signal(SIGFPE, SIG_DFL);
|
signal(SIGFPE, SIG_DFL);
|
||||||
signal(SIGSEGV, SIG_DFL);
|
signal(SIGSEGV, SIG_DFL);
|
||||||
signal(SIGTERM, SIG_DFL);
|
signal(SIGTERM, SIG_DFL);
|
||||||
|
signal(SIGPIPE, SIG_DFL);
|
||||||
|
|
||||||
LyX::emergencyCleanup();
|
LyX::emergencyCleanup();
|
||||||
|
|
||||||
@ -250,6 +258,7 @@ void LyX::init(bool gui)
|
|||||||
signal(SIGSEGV, error_handler);
|
signal(SIGSEGV, error_handler);
|
||||||
signal(SIGINT, error_handler);
|
signal(SIGINT, error_handler);
|
||||||
signal(SIGTERM, error_handler);
|
signal(SIGTERM, error_handler);
|
||||||
|
signal(SIGPIPE, error_handler);
|
||||||
|
|
||||||
bool const explicit_userdir = setLyxPaths();
|
bool const explicit_userdir = setLyxPaths();
|
||||||
|
|
||||||
|
252
src/lyxsocket.C
Normal file
252
src/lyxsocket.C
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
* \file lyxsocket.C
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author Lars Gullik Bjønnes
|
||||||
|
* \author Jean-Marc Lasgouttes
|
||||||
|
* \author Angus Leeming
|
||||||
|
* \author John Levon
|
||||||
|
* \author João Luis M. Assirati
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lyxsocket.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include "funcrequest.h"
|
||||||
|
#include "LyXAction.h"
|
||||||
|
#include "lyxfunc.h"
|
||||||
|
|
||||||
|
#include "frontends/lyx_gui.h"
|
||||||
|
|
||||||
|
#include "support/lyxlib.h"
|
||||||
|
#include "support/socktools.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::endl;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
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 "
|
||||||
|
<< 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_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string const & LyXServerSocket::address() const
|
||||||
|
{
|
||||||
|
return address_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a new LyXDataSocket and checks to see if the connection
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete client;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reads and processes input from client and check
|
||||||
|
// if the connection has been closed
|
||||||
|
void LyXServerSocket::dataCallback(LyXDataSocket * client)
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
string::size_type pos;
|
||||||
|
bool saidbye = false;
|
||||||
|
while ((!saidbye) && client->readln(line)) {
|
||||||
|
// The protocol must be programmed here
|
||||||
|
// Split the key and the data
|
||||||
|
if ((pos = line.find(':')) == string::npos) {
|
||||||
|
client->writeln("ERROR:" + line + ":malformed message");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string const key = line.substr(0, pos);
|
||||||
|
if (key == "LYXCMD") {
|
||||||
|
string const cmd = line.substr(pos + 1);
|
||||||
|
func->dispatch(lyxaction.lookupFunc(cmd));
|
||||||
|
string const rval = func->getMessage();
|
||||||
|
if (func->errorStat()) {
|
||||||
|
client->writeln("ERROR:" + cmd + ':' + rval);
|
||||||
|
} else {
|
||||||
|
client->writeln("INFO:" + cmd + ':' + rval);
|
||||||
|
}
|
||||||
|
} else if (key == "HELLO") {
|
||||||
|
// no use for client name!
|
||||||
|
client->writeln("HELLO:");
|
||||||
|
} else if (key == "BYE") {
|
||||||
|
saidbye = true;
|
||||||
|
} else {
|
||||||
|
client->writeln("ERROR:unknown key " + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saidbye || (!client->connected())) {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Removes client callback and deletes client object
|
||||||
|
void LyXServerSocket::close(LyXDataSocket * client)
|
||||||
|
{
|
||||||
|
lyx_gui::remove_datasocket_callback(client);
|
||||||
|
clients.erase(client);
|
||||||
|
delete client;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
LyXDataSocket::LyXDataSocket(LyXServerSocket * serv)
|
||||||
|
:server_(serv),
|
||||||
|
fd_(lyx::support::socktools::accept(serv->fd()))
|
||||||
|
{
|
||||||
|
if (fd_ == -1) {
|
||||||
|
connected_ = false;
|
||||||
|
} else {
|
||||||
|
lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
|
||||||
|
connected_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LyXDataSocket::connected() const
|
||||||
|
{
|
||||||
|
return connected_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if there was a complete line to input
|
||||||
|
bool LyXDataSocket::readln(string & line)
|
||||||
|
{
|
||||||
|
int const charbuf_size = 100;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error conditions. The buffer must still be
|
||||||
|
// processed for lines read
|
||||||
|
if (count == 0) { // EOF -- connection closed
|
||||||
|
lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
|
||||||
|
<< ": connection closed." << endl;
|
||||||
|
connected_ = false;
|
||||||
|
} else if ((count == -1) && (errno != EAGAIN)) { // IO error
|
||||||
|
lyxerr << "lyx: Data socket " << fd_
|
||||||
|
<< ": IO error." << endl;
|
||||||
|
connected_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut a line from buffer
|
||||||
|
if ((pos = buffer.find('\n')) == 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);
|
||||||
|
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.
|
||||||
|
if ((written == -1) && (errno == EPIPE)) {
|
||||||
|
// The program will also receive a SIGPIPE
|
||||||
|
// that must be catched
|
||||||
|
lyxerr << "lyx: Data 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: Data socket " << fd_
|
||||||
|
<< " IO error: " << strerror(errno);
|
||||||
|
}
|
||||||
|
connected_ = false;
|
||||||
|
}
|
||||||
|
}
|
96
src/lyxsocket.h
Normal file
96
src/lyxsocket.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// -*- C++ -*-
|
||||||
|
/**
|
||||||
|
* \file lyxsocket.h
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author Lars Gullik Bjønnes
|
||||||
|
* \author Jean-Marc Lasgouttes
|
||||||
|
* \author João Luis M. Assirati
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LYXSOCKET_H
|
||||||
|
#define LYXSOCKET_H
|
||||||
|
|
||||||
|
#include "support/socktools.h"
|
||||||
|
#include "lyxfunc.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class LyXServerSocket;
|
||||||
|
class LyXDataSocket;
|
||||||
|
|
||||||
|
/** Sockets can be in two states: listening and connected.
|
||||||
|
* Connected sockets are used to transfer data, and will therefore
|
||||||
|
* be called Data Sockets. Listening sockets are used to create
|
||||||
|
* Data Sockets when clients connect, and therefore will be called
|
||||||
|
* Server Sockets.
|
||||||
|
|
||||||
|
* This class encapsulates local (unix) server socket operations and
|
||||||
|
* manages LyXDataSockets objects that are created when clients connect.
|
||||||
|
*/
|
||||||
|
class LyXServerSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LyXServerSocket(LyXFunc *, std::string const &);
|
||||||
|
~LyXServerSocket();
|
||||||
|
/// File descriptor of the socket
|
||||||
|
int fd() const;
|
||||||
|
/// Address of the local socket
|
||||||
|
std::string const & address() const;
|
||||||
|
/// To be called when there is activity in the server socket
|
||||||
|
void serverCallback();
|
||||||
|
/// To be called when there is activity in the data socket
|
||||||
|
void dataCallback(LyXDataSocket *);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Close the connection to the argument client
|
||||||
|
void close(LyXDataSocket *);
|
||||||
|
|
||||||
|
LyXFunc * func;
|
||||||
|
/// File descriptor for the server socket
|
||||||
|
int fd_;
|
||||||
|
/// Stores the socket filename
|
||||||
|
std::string address_;
|
||||||
|
/// Maximum number of simultaneous clients
|
||||||
|
enum {
|
||||||
|
MAX_CLIENTS = 10
|
||||||
|
};
|
||||||
|
/// All connections
|
||||||
|
std::set<LyXDataSocket *> clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** This class encapsulates data socket operations.
|
||||||
|
* It provides read and write IO operations on the socket.
|
||||||
|
*/
|
||||||
|
class LyXDataSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LyXDataSocket(LyXServerSocket *);
|
||||||
|
~LyXDataSocket();
|
||||||
|
/// The object that allocated us
|
||||||
|
LyXServerSocket * server() const;
|
||||||
|
/// File descriptor of the connection
|
||||||
|
int fd() const;
|
||||||
|
/// Connection status
|
||||||
|
bool connected() const;
|
||||||
|
/// Line buffered input from the socket
|
||||||
|
bool readln(std::string &);
|
||||||
|
/// Write the string + '\n' to the socket
|
||||||
|
void writeln(std::string const &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LyXServerSocket * server_;
|
||||||
|
/// File descriptor for the data socket
|
||||||
|
int fd_;
|
||||||
|
/// True if the connection is up
|
||||||
|
bool connected_;
|
||||||
|
/// buffer for input data
|
||||||
|
std::string buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LYXSOCKET_H
|
@ -65,6 +65,8 @@ libsupport_la_SOURCES = \
|
|||||||
rmdir.C \
|
rmdir.C \
|
||||||
snprintf.h \
|
snprintf.h \
|
||||||
snprintf.c \
|
snprintf.c \
|
||||||
|
socktools.C \
|
||||||
|
socktools.h \
|
||||||
sstream.h \
|
sstream.h \
|
||||||
std_istream.h \
|
std_istream.h \
|
||||||
std_ostream.h \
|
std_ostream.h \
|
||||||
|
128
src/support/socktools.C
Normal file
128
src/support/socktools.C
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* \file socktools.C
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author João Luis M. Assirati
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include "socktools.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "lyxlib.h"
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
using std::endl;
|
||||||
|
using std::strerror;
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
|
||||||
|
namespace lyx {
|
||||||
|
namespace support {
|
||||||
|
namespace socktools {
|
||||||
|
|
||||||
|
// Returns a local socket already in the "listen" state (or -1 in case
|
||||||
|
// of error). The first argument is the socket address, the second
|
||||||
|
// is the length of the queue for connections. If successful, a socket
|
||||||
|
// special file 'name' will be created in the filesystem.
|
||||||
|
int listen(string const & name, int queue)
|
||||||
|
{
|
||||||
|
int fd; // File descriptor for the socket
|
||||||
|
sockaddr_un addr; // Structure that hold the socket address
|
||||||
|
|
||||||
|
// We use 'name' to fill 'addr'
|
||||||
|
string::size_type len = name.size();
|
||||||
|
// the field sun_path in sockaddr_un is a char[108]
|
||||||
|
if (len > 107) {
|
||||||
|
lyxerr << "lyx: Socket address '" << name << "' too long."
|
||||||
|
<< endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Synonims for AF_UNIX are AF_LOCAL and AF_FILE
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
name.copy(addr.sun_path, 107);
|
||||||
|
addr.sun_path[len] = '\0';
|
||||||
|
|
||||||
|
// This creates a file descriptor for the socket
|
||||||
|
// Synonims for PF_UNIX are PF_LOCAL and PF_FILE
|
||||||
|
// For local sockets, the protocol is always 0
|
||||||
|
// socket() returns -1 in case of error
|
||||||
|
if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) {
|
||||||
|
lyxerr << "lyx: Could not create socket descriptor: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set NONBLOCK mode for the file descriptor
|
||||||
|
if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
||||||
|
lyxerr << "lyx: Could not set NONBLOCK mode for socket descriptor: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind() gives the local address 'name' for 'fd', also creating
|
||||||
|
// the socket special file in the filesystem. bind() returns -1
|
||||||
|
// in case of error
|
||||||
|
if ((::bind (fd, reinterpret_cast<sockaddr *>(&addr), SUN_LEN(&addr))) == -1) {
|
||||||
|
lyxerr << "lyx: Could not bind address '" << name
|
||||||
|
<< "' to socket descriptor: " << strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
lyx::support::unlink(name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Puts the socket in listen state, that is, ready to accept
|
||||||
|
// connections. The second parameter of listen() defines the
|
||||||
|
// maximum length the queue of pending connections may grow to.
|
||||||
|
// It is not a restriction on the number of connections the socket
|
||||||
|
// can accept. Returns -1 in case of error
|
||||||
|
if (::listen (fd, queue) == -1) {
|
||||||
|
lyxerr << "lyx: Could not put socket in 'listen' state: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
lyx::support::unlink(name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a file descriptor for a new connection from the socket
|
||||||
|
// descriptor 'sd' (or -1 in case of error)
|
||||||
|
int accept(int sd)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
// Returns the new file descriptor or -1 in case of error
|
||||||
|
// Using null pointers for the second and third arguments
|
||||||
|
// dismiss all information about the connecting client
|
||||||
|
if ((fd = accept(sd, reinterpret_cast<sockaddr *>(0), reinterpret_cast<socklen_t *>(0))) == -1) {
|
||||||
|
lyxerr << "lyx: Could not accept connection: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets NONBLOCK mode for the file descriptor
|
||||||
|
if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
||||||
|
lyxerr << "lyx: Could not set NONBLOCK mode for connection: "
|
||||||
|
<< strerror(errno) << endl;
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace socktools
|
||||||
|
} // namespace support
|
||||||
|
} // namespace lyx
|
28
src/support/socktools.h
Normal file
28
src/support/socktools.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// -*- C++ -*-
|
||||||
|
/**
|
||||||
|
* \file socktools.h
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author João Luis M. Assirati
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOCKTOOLS_H
|
||||||
|
#define SOCKTOOLS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace lyx {
|
||||||
|
namespace support {
|
||||||
|
namespace socktools {
|
||||||
|
|
||||||
|
int listen(std::string const &, int);
|
||||||
|
int accept(int);
|
||||||
|
|
||||||
|
} // namespace socktools
|
||||||
|
} // namespace support
|
||||||
|
} // namespace lyx
|
||||||
|
|
||||||
|
#endif // NOT SOCKTOOLS_H
|
Loading…
Reference in New Issue
Block a user