Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Event dispatch class.
22 : *
23 : * Class used to handle events.
24 : */
25 :
26 :
27 : // self
28 : //
29 : #include "eventdispatcher/local_dgram_base.h"
30 :
31 : #include "eventdispatcher/exception.h"
32 :
33 :
34 : // C
35 : //
36 : #include <sys/stat.h>
37 :
38 :
39 : // last include
40 : //
41 : #include <snapdev/poison.h>
42 :
43 :
44 :
45 :
46 : namespace ed
47 : {
48 :
49 :
50 :
51 : /** \brief Initialize a UDP base object.
52 : *
53 : * This function initializes the UDP base object using the address and the
54 : * port as specified.
55 : *
56 : * The port is expected to be a host side port number (i.e. 59200).
57 : *
58 : * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
59 : * address and it can represent a host name or an address defined with
60 : * just numbers. If the address cannot be resolved then an error occurs
61 : * and the constructor throws.
62 : *
63 : * \note
64 : * The socket is open in this process. If you fork() and exec() then the
65 : * socket gets closed by the operating system (i.e. close on exec()).
66 : *
67 : * \warning
68 : * We only make use of the first address found by getaddrinfo(). All
69 : * the other addresses are ignored.
70 : *
71 : * \todo
72 : * Add a constructor that supports a libaddr::addr object instead of
73 : * just a string address.
74 : *
75 : * \exception invalid_parameter
76 : * The \p addr parameter is empty or the port is out of the supported range.
77 : *
78 : * \exception runtime_error
79 : * The server could not be initialized properly. Either the address cannot be
80 : * resolved, the port is incompatible or not available, or the socket could
81 : * not be created.
82 : *
83 : * \param[in] address The address to connect/listen to.
84 : * \param[in] sequential Whether the packets have to be 100% sequential.
85 : * \param[in] close_on_exec Whether the socket has to be closed on execve().
86 : */
87 5 : local_dgram_base::local_dgram_base(
88 : addr::addr_unix const & address
89 : , bool sequential
90 5 : , bool close_on_exec)
91 5 : : f_address(address)
92 : {
93 5 : int type(sequential ? SOCK_SEQPACKET : SOCK_DGRAM);
94 5 : if(close_on_exec)
95 : {
96 5 : type |= SOCK_CLOEXEC;
97 : }
98 :
99 5 : f_socket.reset(socket(AF_UNIX, type, 0));
100 5 : if(f_socket == nullptr)
101 : {
102 : throw runtime_error(
103 : "could not create socket for: \""
104 0 : + f_address.to_uri()
105 0 : + "\".");
106 : }
107 :
108 : // on Linux we can restrict the permissions immediately
109 : //
110 5 : fchmod(f_socket.get(), S_IRUSR | S_IWUSR);
111 :
112 : // get the "MTU" maximum size right away for
113 : // (1) it is really fast; and
114 : // (2) it is going to work right before a first write() but may not
115 : // be if a write() was not yet fully processed
116 : //
117 5 : socklen_t optlen;
118 5 : optlen = sizeof(f_mtu_size);
119 5 : int const r(getsockopt(
120 10 : f_socket.get()
121 : , SOL_SOCKET
122 : , SO_SNDBUF
123 5 : , &f_mtu_size
124 5 : , &optlen));
125 5 : if(r != 0)
126 : {
127 : throw runtime_error(
128 : "could not retrieve \"MTU\" size for: \""
129 0 : + f_address.to_uri()
130 0 : + "\".");
131 : }
132 5 : }
133 :
134 :
135 : /** \brief The local datagram destructor.
136 : *
137 : * To avoid potential errors with virtual destruction, we have a virtual
138 : * destructor in this base class.
139 : */
140 5 : local_dgram_base::~local_dgram_base()
141 : {
142 5 : }
143 :
144 :
145 : /** \brief Retrieve a copy of the socket identifier.
146 : *
147 : * This function return the socket identifier as returned by the socket()
148 : * function. This can be used to change some flags.
149 : *
150 : * \return The socket used by this UDP client.
151 : */
152 16 : int local_dgram_base::get_socket() const
153 : {
154 16 : return f_socket.get();
155 : }
156 :
157 :
158 : /** \brief Set whether this UDP socket is to be used to broadcast messages.
159 : *
160 : * This function sets the BROADCAST flagon the socket. This is important
161 : * because by default it is expected that the socket is not used in
162 : * broadcast mode. This makes sure that was your intention.
163 : *
164 : * \note
165 : * We do not try to automatically set the flag for (1) the OS implementation
166 : * expects the end user application to systematically set the flag if
167 : * required and (2) it's complicated to know whether the address represents
168 : * the broadcast address (i.e. you need to get the info on the corresponding
169 : * interface to get the network mask, see whether the interface supports
170 : * broadcasting, etc.) We'll eventually implement that test in our
171 : * libaddr library one day. However, that would be a test we use in the
172 : * send() function to catch errors early (i.e. determine whether the
173 : * socket can be sent to in the current state).
174 : *
175 : * \param[in] state Whether to set (true) or remove (false) the broadcast
176 : * flag on this Unix datagram socket.
177 : */
178 0 : void local_dgram_base::set_broadcast(bool state)
179 : {
180 0 : int const value(state ? 1 : 0);
181 0 : setsockopt(f_socket.get(), SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
182 0 : }
183 :
184 :
185 : /** \brief Retrieve the size of the MTU on that connection.
186 : *
187 : * The "MTU" of the AF_UNIX message is defined by the largest allocatable
188 : * page of memory. This is defined in this file:
189 : *
190 : * /proc/sys/net/core/wmem_max
191 : *
192 : * Note that to get the maximum size of your message, you want to use
193 : * the get_mss_size() instead. The MTU size is the entire packet including
194 : * headers.
195 : *
196 : * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
197 : */
198 0 : int local_dgram_base::get_mtu_size() const
199 : {
200 0 : return f_mtu_size;
201 : }
202 :
203 :
204 : /** \brief Determine the size of the data buffer we can use.
205 : *
206 : * This function gets the MTU and then subtract the possible header data
207 : * of the packet to
208 : *
209 : * \return The size of the MMU, which is the MTU minus the headers.
210 : */
211 0 : int local_dgram_base::get_mss_size() const
212 : {
213 0 : int const mss(get_mtu_size());
214 0 : return mss < 32 ? -1 : mss - 32; // it looks like the header uses 32 bytes
215 : }
216 :
217 :
218 : /** \brief Retrieve a copy of the address.
219 : *
220 : * This function returns a copy of the address as it was specified in the
221 : * constructor. This does not return a canonicalized version of the address.
222 : *
223 : * The address cannot be modified. If you need to send data on a different
224 : * address, create a new UDP client.
225 : *
226 : * \return A string with a copy of the constructor input address.
227 : */
228 0 : addr::addr_unix local_dgram_base::get_address() const
229 : {
230 0 : return f_address;
231 : }
232 :
233 :
234 :
235 : } // namespace ed
236 : // vim: ts=4 sw=4 et
|