E-MailRelay
gserver.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
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/// \file gserver.cpp
19///
20
21#include "gdef.h"
22#include "gserver.h"
23#include "gnetdone.h"
24#include "gmonitor.h"
26#include "gcleanup.h"
27#include "gprocess.h"
28#include "glimits.h"
29#include "groot.h"
30#include "glog.h"
31#include "gassert.h"
32#include <algorithm>
33
34GNet::Server::Server( ExceptionSink es , const Address & listening_address ,
35 ServerPeerConfig server_peer_config , ServerConfig server_config ) :
36 m_es(es) ,
37 m_server_peer_config(server_peer_config) ,
38 m_socket(listening_address.family(),StreamSocket::Listener())
39{
40 G_DEBUG( "GNet::Server::ctor: listening on socket " << m_socket.asString()
41 << " with address " << listening_address.displayString() ) ;
42
43 bool uds = listening_address.family() == Address::Family::local ;
44 if( uds )
45 {
46 bool open = server_config.uds_open_permissions ;
47 using Mode = G::Process::Umask::Mode ;
48 G::Root claim_root( false ) ; // group ownership from the effective group-id
49 G::Process::Umask set_umask( open ? Mode::Open : Mode::Tighter ) ;
50 m_socket.bind( listening_address ) ;
51 }
52 else
53 {
54 G::Root claim_root ;
55 m_socket.bind( listening_address ) ;
56 }
57
58 m_socket.listen( std::max(1,server_config.listen_queue) ) ;
59 m_socket.addReadHandler( *this , m_es ) ;
60 Monitor::addServer( *this ) ;
61
62 if( uds )
63 {
64 std::string path = listening_address.hostPartString( true ) ;
65 if( path.size() > 1U && path.at(0U) == '/' ) // (just in case)
66 {
67 G::Cleanup::add( &Server::unlink , G::Cleanup::strdup(path) ) ;
68 }
69 }
70}
71
73{
74 Monitor::removeServer( *this ) ;
75}
76
77bool GNet::Server::canBind( const Address & address , bool do_throw )
78{
79 std::string reason ;
80 {
81 G::Root claim_root ;
82 reason = Socket::canBindHint( address ) ;
83 }
84 if( !reason.empty() && do_throw )
85 throw CannotBind( address.displayString() , reason ) ;
86 return reason.empty() ;
87}
88
90{
91 bool with_scope = true ; // was false
92 Address result = m_socket.getLocalAddress() ;
93 if( with_scope )
94 result.setScopeId( m_socket.getBoundScopeId() ) ;
95 return result ;
96}
97
98void GNet::Server::readEvent()
99{
100 // read-event-on-listening-port => new connection to accept
101 G_DEBUG( "GNet::Server::readEvent: " << this ) ;
102
103 // accept the connection
104 //
105 ServerPeerInfo peer_info( this , m_server_peer_config ) ;
106 accept( peer_info ) ;
107
108 // do an early set of the logging context so that it applies
109 // during the newPeer() construction process -- it is then set
110 // more normally by the event hander list when handing out events
111 // with a well-defined ExceptionSource
112 //
113 EventLoggingContext event_logging_context( peer_info.m_address.hostPartString() ) ;
114 G_DEBUG( "GNet::Server::readEvent: new connection from " << peer_info.m_address.displayString()
115 << " on " << peer_info.m_socket->asString() ) ;
116
117 // create the peer object -- newPeer() implementations will normally catch
118 // their exceptions and return null to avoid terminating the server -- peer
119 // objects are given this object as their exception sink so we get
120 // to delete them when they throw -- the exception sink is passed as
121 // 'unbound' to force the peer object to set themselves as the exception
122 // source
123 //
124 std::unique_ptr<ServerPeer> peer = newPeer( ExceptionSinkUnbound(this) , peer_info ) ;
125
126 // commit or roll back
127 if( peer == nullptr )
128 {
129 G_WARNING( "GNet::Server::readEvent: connection rejected from " << peer_info.m_address.displayString() ) ;
130 }
131 else
132 {
133 G_DEBUG( "GNet::Server::readEvent: new connection accepted" ) ;
134 m_peer_list.push_back( std::shared_ptr<ServerPeer>(peer.release()) ) ;
135 }
136}
137
138void GNet::Server::accept( ServerPeerInfo & peer_info )
139{
140 AcceptPair accept_pair ;
141 {
142 G::Root claim_root ;
143 accept_pair = m_socket.accept() ;
144 }
145 peer_info.m_socket = accept_pair.socket_ptr ;
146 peer_info.m_address = accept_pair.address ;
147}
148
149void GNet::Server::onException( ExceptionSource * esrc , std::exception & e , bool done )
150{
151 G_DEBUG( "GNet::Server::onException: exception=[" << e.what() << "] esrc=[" << static_cast<void*>(esrc) << "]" ) ;
152 bool handled = false ;
153 if( esrc != nullptr )
154 {
155 for( auto list_p = m_peer_list.begin() ; list_p != m_peer_list.end() ; ++list_p )
156 {
157 if( (*list_p).get() == esrc ) // implicit ServerPeer/ExceptionSource static cast
158 {
159 std::shared_ptr<ServerPeer> peer_p = *list_p ;
160 m_peer_list.erase( list_p ) ; // remove first, in case onDelete() throws
161 (*peer_p).doOnDelete( e.what() , done ) ;
162 handled = true ;
163 break ; // ServerPeer deleted here
164 }
165 }
166 }
167 if( !handled )
168 {
169 G_WARNING( "GNet::Server::onException: unhandled exception: " << e.what() ) ;
170 throw ; // should never get here -- rethrow just in case
171 }
172}
173
175{
176 m_peer_list.clear() ;
177}
178
180{
181 return !m_peer_list.empty() ;
182}
183
184std::vector<std::weak_ptr<GNet::ServerPeer> > GNet::Server::peers()
185{
186 using Peers = std::vector<std::weak_ptr<ServerPeer> > ;
187 Peers result ;
188 result.reserve( m_peer_list.size() ) ;
189 for( auto & peer : m_peer_list )
190 result.push_back( std::weak_ptr<ServerPeer>(peer) ) ;
191 return result ;
192}
193
194void GNet::Server::writeEvent()
195{
196 G_DEBUG( "GNet::Server::writeEvent" ) ;
197}
198
199bool GNet::Server::unlink( G::SignalSafe , const char * path ) noexcept
200{
201 return path ? ( std::remove(path) == 0 ) : true ;
202}
203
204// ===
205
206GNet::ServerPeerInfo::ServerPeerInfo( Server * server , ServerPeerConfig config ) :
207 m_address(Address::defaultAddress()) ,
208 m_config(config) ,
209 m_server(server)
210{
211}
212
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
Address & setScopeId(unsigned long)
Sets the scope-id.
Definition: gaddress.cpp:270
Family family() const
Returns the address family enumeration.
Definition: gaddress.cpp:491
std::string hostPartString(bool raw=false) const
Returns a string which represents the network address.
Definition: gaddress.cpp:384
std::string displayString(bool with_scope_id=false) const
Returns a string which represents the transport address.
Definition: gaddress.cpp:375
A class that sets the G::LogOuput::context() while in scope.
A potential ExceptionSink that is realised by bind()ing an exception source pointer.
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
An interface for a network listener.
Definition: glistener.h:37
static void addServer(const Listener &server)
Adds a server.
Definition: gmonitor.cpp:145
static void removeServer(const Listener &server) noexcept
Removes a server.
Definition: gmonitor.cpp:154
A structure that GNet::Server uses to configure its ServerPeer objects.
Definition: gserverpeer.h:51
A structure used in GNet::Server::newPeer().
Definition: gserver.h:142
~Server() override
Destructor.
Definition: gserver.cpp:72
Address address() const override
Returns the listening address.
Definition: gserver.cpp:89
bool hasPeers() const
Returns true if peers() is not empty.
Definition: gserver.cpp:179
Server(ExceptionSink, const Address &listening_address, ServerPeerConfig, ServerConfig)
Constructor.
Definition: gserver.cpp:34
std::vector< std::weak_ptr< GNet::ServerPeer > > peers()
Returns the list of ServerPeer objects.
Definition: gserver.cpp:184
void serverCleanup()
Should be called by the most-derived class's destructor in order to trigger early deletion of peer ob...
Definition: gserver.cpp:174
static bool canBind(const Address &listening_address, bool do_throw)
Checks that the specified address can be bound.
Definition: gserver.cpp:77
std::string asString() const
Returns the socket handle as a string.
Definition: gsocket.cpp:222
void addReadHandler(EventHandler &, ExceptionSink)
Adds this socket to the event source list so that the given handler receives read events.
Definition: gsocket.cpp:172
void bind(const Address &)
Binds the socket with the given address.
Definition: gsocket.cpp:241
static std::string canBindHint(const Address &address, bool stream_socket=true)
Returns the empty string if a socket could probably be bound with the given address or a failure reas...
void listen(int backlog=1)
Starts the socket listening on the bound address for incoming connections or incoming datagrams.
Definition: gsocket.cpp:316
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:311
static const char * strdup(const char *)
A strdup() function that makes it clear in the stack trace that leaks are expected.
static void add(bool(*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...
Used to temporarily modify the process umask.
Definition: gprocess.h:158
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:52
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
A configuration structure for GNet::Server.
Definition: gserver.h:50