- Suppports IPv4 and IPv6 task server addresses.
- Cleaned up error message when server is not available.
This commit is contained in:
Paul Beckingham 2012-10-14 02:04:56 -04:00
parent 47d94f370e
commit cda35c03d6
4 changed files with 123 additions and 94 deletions

View file

@ -10,6 +10,7 @@ Features
+ Adds a new 'synchronize' command to sync data with a task server.
+ Adds a new 'sync' verbosity token, which will reminds when a backlog builds
up and needs a sync.
+ Supports IPv4 and IPv6 server addresses.
Bugs
+ Fixed bug so that 'limit:page' now considers footnote messages.

View file

@ -29,45 +29,29 @@
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <Socket.h>
////////////////////////////////////////////////////////////////////////////////
Socket::Socket (int family, int type, int protocol) :
_port (0),
_family (family),
Socket::Socket () :
_socket (0),
_debug (false),
_limit (0) // Unlimited
_limit (0), // Unlimited
_debug (false)
{
// family: AF_INET (IPv4), AF_INET6 (IPv6), AF_LOCAL, AF_ROUTE, AF_KEY.
// type: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SOCK_RAW, SOCK_PACKET (Linux).
// protocol: IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP.
if ((_socket = ::socket (family, type, protocol)) < 0)
// This looks like a blank error message, but don't forget that the Error
// class will use ::strerror and append a system error message if errno is
// non-zero.
throw "ERROR: " + std::string (::strerror (errno));
// When a socket is closed, it remains unavailable for a while (netstat -an).
// Setting SO_REUSEADDR allows this program to assume control of a closed, but
// unavailable socket.
int on = 1;
if (::setsockopt (_socket, SOL_SOCKET, SO_REUSEADDR, (const void*)&on, sizeof (on)) < 0)
throw "ERROR: " + std::string (::strerror (errno));
}
////////////////////////////////////////////////////////////////////////////////
Socket::Socket (int s) :
_port (0),
_family (AF_UNSPEC),
_socket (s),
_debug (false),
_limit (0) // Unlimited
_limit (0), // Unlimited
_debug (false)
{
}
@ -79,49 +63,50 @@ Socket::~Socket ()
////////////////////////////////////////////////////////////////////////////////
// For clients.
void Socket::connect (const std::string& host, const int port)
void Socket::connect (const std::string& host, const std::string& port)
{
_port = port;
std::string machine;
// use IPv4 or IPv6, does not matter.
struct addrinfo hints;
memset (&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
// If the address is of the form \d+\.\d+\.\d+\.\d+
bool ip = true;
for (unsigned int i = 0; i < host.length (); ++i)
if (!isdigit (host[i]) && host[i] != '.')
struct addrinfo* res;
if (::getaddrinfo (host.c_str (), port.c_str (), &hints, &res) != 0)
throw "ERROR: " + std::string (::gai_strerror (errno));
// Try them all, stop on success.
struct addrinfo* p;
for (p = res; p != NULL; p = p->ai_next)
{
if ((_socket = ::socket (p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
continue;
// When a socket is closed, it remains unavailable for a while (netstat -an).
// Setting SO_REUSEADDR allows this program to assume control of a closed,
// but unavailable socket.
int on = 1;
if (::setsockopt (_socket,
SOL_SOCKET,
SO_REUSEADDR,
(const void*) &on,
sizeof (on)) == -1)
throw "ERROR: " + std::string (::strerror (errno));
if (::connect (_socket, p->ai_addr, p->ai_addrlen) == -1)
{
ip = false;
break;
close ();
continue;
}
if (ip)
{
struct in_addr in;
in.s_addr = inet_addr (host.c_str ());
struct hostent* he = gethostbyaddr ((char*) &in,
sizeof (in.s_addr),
AF_INET);
if (!he)
throw std::string ("ERROR: Cannot resolve host from ") + host;
machine = he->h_name;
}
else
{
struct hostent* he = gethostbyname (host.c_str ());
if (!he)
throw std::string ("ERROR: Cannot resolve host from ") + host;
machine = inet_ntoa (*((struct in_addr*)he->h_addr));
break;
}
struct sockaddr_in server = {0};
server.sin_family = _family;
server.sin_port = htons (_port);
if (::inet_pton (_family, machine.c_str (), &server.sin_addr) != 1)
throw "ERROR: " + std::string (::strerror (errno));
free (res);
if (::connect (_socket, (struct sockaddr*) &server, sizeof (server)) < 0)
throw "ERROR: " + std::string (::strerror (errno));
if (p == NULL)
throw "ERROR: Could not connect to " + host + " " + port;
}
////////////////////////////////////////////////////////////////////////////////
@ -134,15 +119,36 @@ void Socket::close ()
////////////////////////////////////////////////////////////////////////////////
// For servers.
void Socket::bind (int family, int port)
void Socket::bind (const std::string& port)
{
struct sockaddr_in server;
memset (&server, 0, sizeof (server));
server.sin_family = _family = family;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (_port = port);
// use IPv4 or IPv6, does not matter.
struct addrinfo hints;
memset (&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if (::bind (_socket, (struct sockaddr*) &server, sizeof (server)) < 0)
struct addrinfo* res;
if (::getaddrinfo (NULL, port.c_str (), &hints, &res) != 0)
throw "ERROR: " + std::string (::gai_strerror (errno));
if ((_socket = ::socket (res->ai_family,
res->ai_socktype,
res->ai_protocol)) == -1)
throw "ERROR: Can not bind to port " + port;
// When a socket is closed, it remains unavailable for a while (netstat -an).
// Setting SO_REUSEADDR allows this program to assume control of a closed, but
// unavailable socket.
int on = 1;
if (::setsockopt (_socket,
SOL_SOCKET,
SO_REUSEADDR,
(const void*) &on,
sizeof (on)) == -1)
throw "ERROR: " + std::string (::strerror (errno));
if (::bind (_socket, res->ai_addr, res->ai_addrlen) == -1)
throw "ERROR: " + std::string (::strerror (errno));
}
@ -156,8 +162,8 @@ void Socket::listen (int queue /*= 5*/)
////////////////////////////////////////////////////////////////////////////////
int Socket::accept ()
{
struct sockaddr_in client;
socklen_t length = sizeof (client);
struct sockaddr_storage client;
socklen_t length = sizeof client;
int connection;
do
@ -288,4 +294,14 @@ void Socket::debug ()
}
////////////////////////////////////////////////////////////////////////////////
// get sockaddr, IPv4 or IPv6:
void* Socket::get_in_addr (struct sockaddr* sa)
{
if (sa->sa_family == AF_INET)
return &(((struct sockaddr_in*) sa)->sin_addr);
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -38,25 +38,32 @@
class Socket
{
public:
Socket (int, int, int);
Socket ();
Socket (int);
~Socket ();
void connect (const std::string&, const int);
void close ();
void bind (int, int);
// Client
void connect (const std::string&, const std::string&);
// Server
void bind (const std::string&);
void listen (int queue = 5);
int accept ();
void read (std::string&);
void write (const std::string&);
void close ();
void limit (int);
void debug ();
private:
int _port;
int _family;
void* get_in_addr (struct sockaddr*);
private:
int _socket;
bool _debug;
int _limit;
bool _debug;
};
#endif

View file

@ -28,6 +28,7 @@
#define L10N // Localization complete.
#include <iostream>
#include <sstream>
#include <inttypes.h>
#include <Context.h>
#include <Socket.h>
@ -54,10 +55,12 @@ int CmdSync::execute (std::string& output)
int status = 0;
context.timer_sync.start ();
std::stringstream out;
// If no server is set up, quit.
std::string connection = context.config.get ("taskd.server");
if (connection == "" ||
connection.find (':') == std::string::npos)
connection.rfind (':') == std::string::npos)
throw std::string (STRING_CMD_SYNC_NO_SERVER);
// Obtain credentials.
@ -99,8 +102,8 @@ int CmdSync::execute (std::string& output)
request.setPayload (payload);
std::cout << format (STRING_CMD_SYNC_PROGRESS, connection)
<< "\n";
out << format (STRING_CMD_SYNC_PROGRESS, connection)
<< "\n";
Msg response;
if (send (connection, request, response))
@ -135,22 +138,22 @@ int CmdSync::execute (std::string& output)
Task dummy;
if (context.tdb2.get (uuid, dummy))
{
std::cout << " "
<< colorChanged.colorize (
format (STRING_CMD_SYNC_MOD,
uuid,
from_server.get ("description")))
<< "\n";
out << " "
<< colorChanged.colorize (
format (STRING_CMD_SYNC_MOD,
uuid,
from_server.get ("description")))
<< "\n";
context.tdb2.modify (from_server, false);
}
else
{
std::cout << " "
<< colorAdded.colorize (
format (STRING_CMD_SYNC_ADD,
uuid,
from_server.get ("description")))
<< "'\n";
out << " "
<< colorAdded.colorize (
format (STRING_CMD_SYNC_ADD,
uuid,
from_server.get ("description")))
<< "\n";
context.tdb2.add (from_server, false);
}
}
@ -228,6 +231,8 @@ int CmdSync::execute (std::string& output)
status = 1;
}
out << "\n";
output = out.str ();
context.timer_sync.stop ();
return status;
}
@ -238,16 +243,16 @@ bool CmdSync::send (
const Msg& request,
Msg& response)
{
std::string::size_type colon = to.find (':');
std::string::size_type colon = to.rfind (':');
if (colon == std::string::npos)
throw format (STRING_CMD_SYNC_BAD_SERVER, to);
std::string server = to.substr (0, colon);
int port = strtoimax (to.substr (colon + 1).c_str (), NULL, 10);
std::string port = to.substr (colon + 1);
try
{
Socket s (AF_INET, SOCK_STREAM, IPPROTO_TCP);
Socket s;
s.connect (server, port);
s.write (request.serialize () + "\n");
@ -261,7 +266,7 @@ bool CmdSync::send (
catch (std::string& error)
{
context.error (error);
context.debug (error);
}
// Indicate message failed.