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