E-MailRelay

gmsg_unix.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 gmsg_unix.cpp
19///
20
21#include "gdef.h"
22#include "gmsg.h"
23#include "gprocess.h"
24#include "gassert.h"
25#include <cstring> // std::memcpy()
26#include <cerrno> // EINTR etc
27#include <algorithm>
28#include <iterator>
29#include <array>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/uio.h> // struct iovec
33
34 namespace G
35{
36 namespace MsgImp
37 {
38 template <typename Tin, typename Tout, typename Fn1, typename Fn2>
39 std::size_t copy( Tin in , Tout out , Fn1 fn_convert , Fn2 fn_empty )
40 {
41 return std::distance( out.begin() ,
42 std::remove_if( out.begin() , std::transform( in.begin() , in.end() , out.begin() , fn_convert ) , fn_empty ) ) ;
43 }
44 ssize_t sendmsg( int fd , const iovec * , std::size_t , int flags ,
45 const sockaddr * address_p , socklen_t address_n , int fd_to_send ) noexcept ;
46 }
47}
48
49ssize_t G::Msg::send( int fd , const void * buffer , std::size_t size , int flags ) noexcept
50{
51 return sendto( fd , buffer , size , flags , nullptr , 0U ) ;
52}
53
54ssize_t G::Msg::sendto( int fd , const void * buffer , std::size_t size , int flags ,
55 const sockaddr * address_p , socklen_t address_n ) noexcept
56{
57 return ::sendto( fd , buffer , size , flags|MSG_NOSIGNAL , // NOLINT
58 const_cast<sockaddr*>(address_p) , address_n ) ;
59}
60
61ssize_t G::Msg::sendto( int fd , const std::vector<std::string_view> & data , int flags ,
62 const sockaddr * address_p , socklen_t address_n )
63{
64 if( data.empty() )
65 {
66 return 0 ;
67 }
68 else if( data.size() == 1U )
69 {
70 return sendto( fd , data[0].data() , data[0].size() , flags , address_p , address_n ) ;
71 }
72 else
73 {
74 std::array<::iovec,40> iovec_array ;
75 auto fn_convert = [](std::string_view sv){ ::iovec i; i.iov_len=sv.size(); i.iov_base=sv.empty()?nullptr:const_cast<char*>(sv.data()); return i; } ;
76 auto fn_empty = [](const ::iovec &i){ return i.iov_base == nullptr; } ;
77 if( data.size() <= iovec_array.size() )
78 {
79 std::size_t n = MsgImp::copy( data , iovec_array , fn_convert , fn_empty ) ;
80 return n ? MsgImp::sendmsg( fd , iovec_array.data() , n , flags , address_p , address_n , -1 ) : 0U ;
81 }
82 else
83 {
84 std::vector<::iovec> iovec_vector( data.size() ) ;
85 std::size_t n = MsgImp::copy( data , iovec_vector , fn_convert , fn_empty ) ;
86 return n ? MsgImp::sendmsg( fd , iovec_vector.data() , n , flags , address_p , address_n , -1 ) : 0U ;
87 }
88 }
89}
90
91#ifndef G_LIB_SMALL
92ssize_t G::Msg::sendto( int fd , const void * buffer , std::size_t size , int flags ,
93 const sockaddr * address_p , socklen_t address_n , int fd_to_send )
94{
95 if( fd_to_send == -1 )
96 {
97 return sendto( fd , buffer , size , flags , address_p , address_n ) ;
98 }
99 else
100 {
101 struct ::iovec io {} ;
102 io.iov_base = const_cast<void*>(buffer) ;
103 io.iov_len = size ;
104 return MsgImp::sendmsg( fd , &io , 1U , flags , address_p , address_n , fd_to_send ) ;
105 }
106}
107#endif
108
109ssize_t G::MsgImp::sendmsg( int fd , const ::iovec * iovec_p , std::size_t iovec_n , int flags ,
110 const sockaddr * address_p , socklen_t address_n , int fd_to_send ) noexcept
111{
112 struct ::msghdr msg {} ;
113
114 msg.msg_name = const_cast<sockaddr*>(address_p) ;
115 msg.msg_namelen = address_n ;
116 msg.msg_iov = const_cast<::iovec*>(iovec_p) ;
117 msg.msg_iovlen = iovec_n ;
118
119 // CMSG_SPACE() is not a compile-time constant on OSX -- see gmsg_mac.cpp
120
121 constexpr std::size_t space = CMSG_SPACE( sizeof(int) ) ;
122 static_assert( space != 0U , "" ) ;
123 std::array<char,space> control_buffer {} ;
124 std::memset( control_buffer.data() , 0 , control_buffer.size() ) ;
125 msg.msg_control = control_buffer.data() ;
126 msg.msg_controllen = control_buffer.size() ;
127
128 struct ::cmsghdr * cmsg = CMSG_FIRSTHDR( &msg ) ; /// NOLINT
129 G_ASSERT( cmsg != nullptr ) ;
130 if( cmsg != nullptr )
131 {
132 cmsg->cmsg_len = CMSG_LEN( sizeof(int) ) ;
133 cmsg->cmsg_level = SOL_SOCKET ;
134 cmsg->cmsg_type = SCM_RIGHTS ;
135 std::memcpy( CMSG_DATA(cmsg) , &fd_to_send , sizeof(int) ) ;
136 }
137
138 return ::sendmsg( fd , &msg , flags | MSG_NOSIGNAL ) ; // NOLINT
139}
140
141ssize_t G::Msg::recv( int fd , void * buffer , std::size_t size , int flags ) noexcept
142{
143 return ::recv( fd , buffer , size , flags ) ;
144}
145
146ssize_t G::Msg::recvfrom( int fd , void * buffer , std::size_t size , int flags ,
147 sockaddr * address_p , socklen_t * address_np ) noexcept
148{
149 return ::recvfrom( fd , buffer , size , flags , address_p , address_np ) ;
150}
151
152#ifndef G_LIB_SMALL
153ssize_t G::Msg::recvfrom( int fd , void * buffer , std::size_t size , int flags ,
154 sockaddr * address_p , socklen_t * address_np , int * fd_received_p )
155{
156 if( fd_received_p == nullptr )
157 return recvfrom( fd , buffer , size , flags , address_p , address_np ) ;
158
159 struct ::msghdr msg {} ;
160
161 msg.msg_name = address_p ;
162 msg.msg_namelen = address_np == nullptr ? socklen_t(0) : *address_np ;
163
164 struct ::iovec io {} ;
165 io.iov_base = buffer ;
166 io.iov_len = size ;
167 msg.msg_iov = &io ;
168 msg.msg_iovlen = 1 ;
169
170 std::array<char,CMSG_SPACE(sizeof(int))> control_buffer {} ;
171 msg.msg_control = control_buffer.data() ;
172 msg.msg_controllen = control_buffer.size() ;
173
174 ssize_t rc = ::recvmsg( fd , &msg , flags ) ;
175 int e = Process::errno_() ;
176 if( rc >= 0 && msg.msg_controllen > 0U && fd_received_p != nullptr )
177 {
178 struct cmsghdr * cmsg = CMSG_FIRSTHDR( &msg ) ; /// NOLINT
179 if( cmsg != nullptr && cmsg->cmsg_type == SCM_RIGHTS )
180 {
181 std::memcpy( fd_received_p , CMSG_DATA(cmsg) , sizeof(int) ) ;
182 }
183 }
184 if( rc >= 0 && address_np != nullptr )
185 {
186 *address_np = msg.msg_namelen ;
187 }
188 Process::errno_( SignalSafe() , e ) ;
189 return rc ; // with errno
190}
191#endif
192
193#ifndef G_LIB_SMALL
194 bool G::Msg::fatal( int error ) noexcept
195{
196 return !(
197 error == 0 ||
198 error == EAGAIN ||
199 error == EINTR ||
200 error == EMSGSIZE || // moot
201 error == ENOBUFS ||
202 error == ENOMEM ) ;
203}
204#endif
205
G::Msg::recv
static ssize_t recv(SOCKET, void *, std::size_t, int flags) noexcept
A recv() wrapper.
Definition: gmsg_mac.cpp:39
G::Msg::fatal
static bool fatal(int error) noexcept
Returns true if the error value indicates a permanent problem with the socket.
Definition: gmsg_mac.cpp:50
G::Msg::send
static ssize_t send(SOCKET, const void *, std::size_t, int flags) noexcept
A send() wrapper.
Definition: gmsg_mac.cpp:27
G::Msg::sendto
static ssize_t sendto(SOCKET, const void *, std::size_t, int flags, const sockaddr *, socklen_t) noexcept
A sendto() wrapper.
Definition: gmsg_mac.cpp:32
G::Msg::recvfrom
static ssize_t recvfrom(SOCKET, void *, std::size_t, int, sockaddr *, socklen_t *) noexcept
A recvfrom() wrapper.
Definition: gmsg_mac.cpp:44
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:125
G::SignalSafe
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
gassert.h
gmsg.h
gprocess.h
G
Low-level classes.
Definition: garg.h:36