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