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