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

View file

@ -29,45 +29,29 @@
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h> #include <sys/errno.h>
#include <netdb.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <Socket.h> #include <Socket.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Socket::Socket (int family, int type, int protocol) : Socket::Socket () :
_port (0),
_family (family),
_socket (0), _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) : Socket::Socket (int s) :
_port (0),
_family (AF_UNSPEC),
_socket (s), _socket (s),
_debug (false), _limit (0), // Unlimited
_limit (0) // Unlimited _debug (false)
{ {
} }
@ -79,49 +63,50 @@ Socket::~Socket ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// For clients. // 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; // use IPv4 or IPv6, does not matter.
std::string machine; 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+ struct addrinfo* res;
bool ip = true; if (::getaddrinfo (host.c_str (), port.c_str (), &hints, &res) != 0)
for (unsigned int i = 0; i < host.length (); ++i) throw "ERROR: " + std::string (::gai_strerror (errno));
if (!isdigit (host[i]) && host[i] != '.')
// Try them all, stop on success.
struct addrinfo* p;
for (p = res; p != NULL; p = p->ai_next)
{ {
ip = false; 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)
{
close ();
continue;
}
break; break;
} }
if (ip) free (res);
{
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; if (p == NULL)
} throw "ERROR: Could not connect to " + host + " " + port;
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));
}
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));
if (::connect (_socket, (struct sockaddr*) &server, sizeof (server)) < 0)
throw "ERROR: " + std::string (::strerror (errno));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -134,15 +119,36 @@ void Socket::close ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// For servers. // For servers.
void Socket::bind (int family, int port) void Socket::bind (const std::string& port)
{ {
struct sockaddr_in server; // use IPv4 or IPv6, does not matter.
memset (&server, 0, sizeof (server)); struct addrinfo hints;
server.sin_family = _family = family; memset (&hints, 0, sizeof hints);
server.sin_addr.s_addr = htonl (INADDR_ANY); hints.ai_family = AF_UNSPEC;
server.sin_port = htons (_port = port); 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)); throw "ERROR: " + std::string (::strerror (errno));
} }
@ -156,8 +162,8 @@ void Socket::listen (int queue /*= 5*/)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int Socket::accept () int Socket::accept ()
{ {
struct sockaddr_in client; struct sockaddr_storage client;
socklen_t length = sizeof (client); socklen_t length = sizeof client;
int connection; int connection;
do 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 class Socket
{ {
public: public:
Socket (int, int, int); Socket ();
Socket (int); Socket (int);
~Socket (); ~Socket ();
void connect (const std::string&, const int);
void close (); // Client
void bind (int, int); void connect (const std::string&, const std::string&);
// Server
void bind (const std::string&);
void listen (int queue = 5); void listen (int queue = 5);
int accept (); int accept ();
void read (std::string&); void read (std::string&);
void write (const std::string&); void write (const std::string&);
void close ();
void limit (int); void limit (int);
void debug (); void debug ();
private: private:
int _port; void* get_in_addr (struct sockaddr*);
int _family;
private:
int _socket; int _socket;
bool _debug;
int _limit; int _limit;
bool _debug;
}; };
#endif #endif

View file

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