VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvcommandsocket.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvcommandsocket.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gvcommandsocket.h"
24 #include "geventhandler.h"
25 #include "gcleanup.h"
26 #include "gstr.h"
27 #include "groot.h"
28 #include "gfile.h"
29 #include "gresolver.h"
30 #include "glimits.h"
31 #include "glocation.h"
32 #include "gassert.h"
33 
34 namespace
35 {
36  char * strdup_ignore_leak( const char * p )
37  {
38  return ::strdup( p ) ;
39  }
40 }
41 
43 {
44 }
45 
46 Gv::CommandSocket::CommandSocket( const std::string & bind_name )
47 {
48  if( bind_name.empty() )
49  {
50  G_ASSERT( fd() == -1 ) ;
51  }
52  else
53  {
54  Type type = parse( bind_name ) ;
55  if( type.net )
56  {
57  GNet::Location location( type.path ) ;
58  std::string e = GNet::Resolver::resolve( location ) ;
59  if( !e.empty() ) throw std::runtime_error( e ) ;
60  m_net.reset( new GNet::DatagramSocket(location.address().domain()) ) ;
61  {
62  G::Root claim_root ;
63  m_net->bind( location.address() ) ;
64  }
65  }
66  else
67  {
68  {
69  G::Root claim_root ;
70  G::File::remove( type.path , G::File::NoThrow() ) ;
71  }
72  m_local.reset( new G::LocalSocket(true) ) ;
73  {
74  G::Root claim_root ;
75  m_local->bind( type.path ) ;
76  }
77  m_local->nonblock() ;
78  G::Cleanup::add( CommandSocket::cleanup , strdup_ignore_leak(type.path.c_str()) ) ;
79  }
80  }
81 }
82 
83 void Gv::CommandSocket::cleanup( G::SignalSafe signal_safe , const char * name )
84 {
85  G::Identity id = G::Root::start( signal_safe ) ;
86  std::remove( name ) ;
87  G::Root::stop( signal_safe , id ) ;
88 }
89 
90 void Gv::CommandSocket::connect( const std::string & connect_name )
91 {
92  Type type = parse( connect_name ) ;
93  if( type.net )
94  {
95  GNet::Location location( type.path ) ;
96  std::string e = GNet::Resolver::resolve( location ) ;
97  if( !e.empty() ) throw std::runtime_error( e ) ;
98  m_net.reset( new GNet::DatagramSocket(location.address().domain()) ) ;
99  {
100  G::Root claim_root ;
101  m_net->connect( location.address() ) ;
102  }
103  }
104  else
105  {
106  m_local.reset( new G::LocalSocket(true) ) ;
107  {
108  G::Root claim_root ;
109  m_local->connect( type.path ) ;
110  }
111  m_local->nonblock() ;
112  }
113 }
114 
115 std::string Gv::CommandSocket::connect( const std::string & connect_name , Gv::CommandSocket::NoThrow )
116 {
117  try
118  {
119  connect( connect_name ) ;
120  return std::string() ;
121  }
122  catch( std::exception & e )
123  {
124  return e.what() ;
125  }
126 }
127 
129 {
130  m_local.reset() ;
131  m_net.reset() ;
132 }
133 
135 {
136  // heuristics to decide whether name is a local-domain path or ip transport address
137 
138  Type type ;
139  type.net = true ;
140  type.path = name ;
141  type.port = 0U ;
142 
143  size_t pos = name.rfind(':') ;
144  std::string tail = G::Str::tail( name , pos , "" ) ;
145  if( !tail.empty() && G::Str::isUInt(tail) )
146  type.port = G::Str::toUInt(tail) ;
147 
148  if( type.port && name.find("udp://") == 0U )
149  {
150  type.path = name.substr(6U) ;
151  }
152  else if( type.port && pos > 3U && name.find("udp:") == 0U )
153  {
154  type.path = name.substr(4U) ;
155  }
156  else if( name.find("unix:") == 0U )
157  {
158  type.net = false ;
159  type.path = name.substr(5U) ;
160  }
161  else if( type.port == 0U || name.find('/') != std::string::npos )
162  {
163  type.net = false ;
164  }
165  return type ;
166 }
167 
169 {
170  if( m_local.get() ) return m_local->fd() ;
171  if( m_net.get() ) return m_net->fd() ;
172  return -1 ;
173 }
174 
176 {
177  std::string result ;
178  m_buffer.resize( G::limits::pipe_buffer ) ;
179  ssize_t n = ::read( fd() , &m_buffer[0] , m_buffer.size() ) ;
180  if( n > 0 )
181  {
182  result.assign( &m_buffer[0] , n ) ;
183  G::Str::trim( result , G::Str::ws() ) ;
184  }
185  return result ;
186 }
187 
188 // ==
189 
190 /// \class Gv::CommandSocketMixinImp
191 /// A pimple-pattern implementation class for Gv::CommandSocketMixin.
192 ///
194 {
195 public:
196  CommandSocketMixinImp( CommandSocketMixin & , const std::string & ) ;
197  virtual ~CommandSocketMixinImp() ;
198 
199 private:
200  virtual void readEvent() override ;
201  virtual void onException( std::exception & ) override ;
202 
203 private:
204  CommandSocketMixin & m_outer ;
205  CommandSocket m_command_socket ;
206  int m_fd ;
207 } ;
208 
209 Gv::CommandSocketMixinImp::CommandSocketMixinImp( CommandSocketMixin & outer , const std::string & bind_name ) :
210  m_outer(outer) ,
211  m_command_socket(bind_name) ,
212  m_fd(m_command_socket.fd())
213 {
214  if( m_fd != -1 )
216 }
217 
218 Gv::CommandSocketMixinImp::~CommandSocketMixinImp()
219 {
220  if( m_fd != -1 )
222 }
223 
224 void Gv::CommandSocketMixinImp::readEvent()
225 {
226  m_outer.onCommandSocketData( m_command_socket.read() ) ;
227 }
228 
229 void Gv::CommandSocketMixinImp::onException( std::exception & )
230 {
231  throw ;
232  //if( m_fd != -1 )
233  //{
234  //GNet::EventLoop::instance().dropRead( GNet::Descriptor(m_fd) ) ;
235  //m_fd = -1 ;
236  //}
237 }
238 
239 Gv::CommandSocketMixin::CommandSocketMixin( const std::string & bind_name ) :
240  m_imp(new CommandSocketMixinImp(*this,bind_name))
241 {
242 }
243 
245 {
246  delete m_imp ;
247 }
248 
Describes a Gv::CommandSocket as a UDP address or unix-domain path.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:36
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
Definition: gidentity.h:42
A derivation of GNet::Socket for a datagram socket.
Definition: gsocket.h:279
Overload descriminator for Gv::CommandSocket.
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
static Identity start(SignalSafe)
A signal-safe alternative to construction.
Definition: groot.cpp:80
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
A mixin base class that contains a bound Gv::CommandSocket object integrated with the GNet::EventLoop...
A class that encapsulates a network file descriptor and hides knowledge of its o/s-spefific error val...
Definition: gdescriptor.h:37
CommandSocket()
Constructor for a sending socket.
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1051
static void stop(SignalSafe, Identity)
A signal-safe alternative to destruction.
Definition: groot.cpp:86
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:208
static Type parse(const std::string &bind_name)
Parses a filesystem path or transport address.
void connect(const std::string &connect_name)
Creates an association with the remote socket, taking a local filesystem path or a transport address...
int fd() const
Returns the file descriptor.
A class that holds a host/service name pair and the preferred address family (if any), and also the results of a name-to-address lookup, ie.
Definition: glocation.h:51
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
Address address() const
Returns the remote address.
Definition: glocation.cpp:124
virtual ~CommandSocketMixin()
Destructor.
and accept()ing should be performed directly on the file descriptor.
Definition: glocalsocket.h:72
int domain() const
Returns the address 'domain', eg. PF_INET.
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:78
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
An overload discriminator class for File methods.
Definition: gfile.h:53
std::string read()
Reads the socket. Returns the empty string on error.
static std::string resolve(Location &)
Does syncronous name resolution.
Definition: gresolver.cpp:385
A non-blocking datagram socket that is used for sending and receiving process control commands...
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
Definition: gstr.cpp:266
void close()
Closes the sending socket, if open.
static std::string ws()
A convenience function returning standard whitespace characters.
Definition: gstr.cpp:1027
static void add(void(*fn)(SignalSafe, const char *), const char *arg)
Adds the given handler to the list of handlers that are to be called when the process terminates abno...
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43
CommandSocketMixin(const std::string &bind_name)
Constructor.
A pimple-pattern implementation class for Gv::CommandSocketMixin.