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 along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Handle an AF_UNIX socket as a server.
22 : *
23 : * This class is used to listen on a stream oriented connection using an
24 : * AF_UNIX type of socket.
25 : */
26 :
27 : // self
28 : //
29 : #include "eventdispatcher/local_stream_server_connection.h"
30 :
31 : #include "eventdispatcher/exception.h"
32 : #include "eventdispatcher/local_stream_client_connection.h"
33 :
34 :
35 : // snapdev
36 : //
37 : #include <snapdev/chownnm.h>
38 :
39 :
40 : // snaplogger
41 : //
42 : #include <snaplogger/message.h>
43 :
44 :
45 : // C
46 : //
47 : #include <fcntl.h>
48 : #include <sys/stat.h>
49 :
50 :
51 : // last include
52 : //
53 : #include <snapdev/poison.h>
54 :
55 :
56 :
57 : namespace ed
58 : {
59 :
60 :
61 :
62 : /** \brief Initialize the local stream server.
63 : *
64 : * The server constructor creates a socket, binds it, and then listen on it.
65 : *
66 : * \todo
67 : * Fix docs.
68 : *
69 : * By default the server accepts a maximum of \p max_connections (set to
70 : * 0 or less to get the default tcp_server::MAX_CONNECTIONS) in its waiting queue.
71 : * If you use the server and expect a low connection rate, you may want to
72 : * reduce the count to 5. Although some very busy servers use larger numbers.
73 : * This value gets clamped to a minimum of 5 and a maximum of 1,000.
74 : *
75 : * Note that the maximum number of connections is actually limited to
76 : * /proc/sys/net/core/somaxconn connections. This number is generally 128
77 : * in 2016. So the super high limit of 1,000 is anyway going to be ignored
78 : * by the OS.
79 : *
80 : * The Unix socket is made non-reusable by default. It is possible to mark
81 : * the server address as reusable by setting the \p force_reuse_addr to true.
82 : * If not true, then the fact that the file exists prevents a new server from
83 : * being created. When true, the function first checks whether it can connect
84 : * to a server. It a connection succeeds, then the creation of the server
85 : * fails (i.e. the Unix socket is already in use). If the connection fails,
86 : * the function makes sure to delete the file if it exists.
87 : *
88 : * By default the server is marked as "keepalive". You can turn it off
89 : * using the keepalive() function with false.
90 : *
91 : * \note
92 : * The code here handles the file based sockets by attempting to delete
93 : * the file if it already exists. If you create services that could end
94 : * up using the exact same socket name, then it could be an issue. If
95 : * not then the code will do exactly what you expects. That being said.
96 : * If you want to increase your chance of proper clean up of a Unix
97 : * socket file, you would have to create a parent process which deletes
98 : * the file whenever the child dies. Then it can either restart the child
99 : * (on a crash) or just quit.
100 : *
101 : * \exception runtime_error
102 : * This exception is raised if the socket cannot be created, bound to
103 : * the specified Unix address, or listen() fails on the socket.
104 : *
105 : * \param[in] address The Unix address to listen on.
106 : * \param[in] max_connections The number of connections to keep in the listen queue.
107 : * \param[in] force_reuse_addr Whether to allow the deletion of a file
108 : * before the bind() call.
109 : * \param[in] close_on_exec Whether to close the server & client sockets
110 : * on an execve().
111 : */
112 1 : local_stream_server_connection::local_stream_server_connection(
113 : addr::addr_unix const & address
114 : , int max_connections
115 : , bool force_reuse_addr
116 1 : , bool close_on_exec)
117 1 : : f_address(address)
118 1 : , f_max_connections(max_connections)
119 1 : , f_close_on_exec(close_on_exec)
120 : {
121 1 : if(f_max_connections < 5)
122 : {
123 0 : f_max_connections = 5;
124 : }
125 1 : else if(f_max_connections > 1000)
126 : {
127 0 : f_max_connections = 1000;
128 : }
129 :
130 1 : sockaddr_un un;
131 1 : f_address.get_un(un);
132 2 : f_socket.reset(socket(
133 1 : un.sun_family
134 : , SOCK_STREAM | SOCK_NONBLOCK | (close_on_exec ? SOCK_CLOEXEC : 0)
135 : , 0));
136 1 : if(f_socket == nullptr)
137 : {
138 0 : int const e(errno);
139 0 : SNAP_LOG_ERROR
140 : << "socket() failed creating a socket descriptor (errno: "
141 0 : << std::to_string(e)
142 : << " -- "
143 0 : << strerror(e)
144 : << "); cannot listen on address \""
145 0 : << f_address.to_uri()
146 : << "\"."
147 : << SNAP_LOG_SEND;
148 0 : throw runtime_error("could not create socket for AF_UNIX server");
149 : }
150 :
151 1 : if(f_address.is_unnamed())
152 : {
153 : // for an unnamed socket, we do not bind at all the user is
154 : // responsible for knowing where to read and where to write
155 : //
156 0 : return;
157 : }
158 :
159 1 : int r(-1);
160 1 : if(f_address.is_file())
161 : {
162 : // a Unix file socket must create a new socket file to prove unicity
163 : // if the file already exists, even if it isn't used, the bind() call
164 : // will fail; if the file exists and the force_reuse_addr is true this
165 : // this function attempts to delete the file if it is a socket and we
166 : // can't connect to it (i.e. "lost file")
167 : //
168 1 : struct stat st = {};
169 1 : if(stat(un.sun_path, &st) == 0)
170 : {
171 0 : if(!S_ISSOCK(st.st_mode))
172 : {
173 0 : SNAP_LOG_ERROR
174 : << "file \""
175 : << un.sun_path
176 : << "\" is not a socket; cannot listen on address \""
177 0 : << f_address.to_uri()
178 : << "\"."
179 : << SNAP_LOG_SEND;
180 0 : throw runtime_error("file already exists and it is not a socket, can't create an AF_UNIX server");
181 : }
182 :
183 0 : bool available(false);
184 0 : if(force_reuse_addr)
185 : {
186 0 : SNAP_LOG_WARNING
187 : << "attempting a contection to "
188 0 : << f_address.to_uri()
189 : << " as a client to see that the address is available for this server;"
190 : << " on success this generates an expected fatal error which we catch here."
191 : << SNAP_LOG_SEND;
192 : try
193 : {
194 : // TODO: this generates a fatal error in the log which can
195 : // be disturbing... we could look at adding a tag on
196 : // that error and here mark the messages associated
197 : // with that tag as hidden so that way we do not get
198 : // the error output.
199 : //
200 0 : local_stream_client_connection test_connection(f_address);
201 0 : }
202 0 : catch(runtime_error const & e)
203 : {
204 : // note: in Linux we can distinguish between a full
205 : // backlog (EAGAIN) and a disconnected socket
206 : // (ECONNREFUSED); we should not set available
207 : // to true on EAGAIN...
208 : //
209 0 : available = true;
210 0 : }
211 : }
212 0 : if(!available)
213 : {
214 0 : SNAP_LOG_ERROR
215 : << "file socket \""
216 : << un.sun_path
217 : << "\" already in use (errno: "
218 0 : << std::to_string(EADDRINUSE)
219 : << " -- "
220 0 : << strerror(EADDRINUSE)
221 : << "); cannot listen on address \""
222 0 : << f_address.to_uri()
223 : << "\"."
224 : << SNAP_LOG_SEND;
225 0 : throw runtime_error("socket already exists, can't create an AF_UNIX server");
226 : }
227 :
228 0 : r = f_address.unlink();
229 0 : if(r != 0
230 0 : && errno != ENOENT)
231 : {
232 0 : SNAP_LOG_ERROR
233 : << "not able to delete file socket \""
234 : << un.sun_path
235 : << "\"; socket already in use (errno: "
236 0 : << std::to_string(EADDRINUSE)
237 : << " -- "
238 0 : << strerror(EADDRINUSE)
239 : << "); cannot listen on address \""
240 0 : << f_address.to_uri()
241 : << "\"."
242 : << SNAP_LOG_SEND;
243 0 : throw runtime_error("could not unlink socket to reuse it as an AF_UNIX server");
244 : }
245 : }
246 :
247 : #ifdef __linux__
248 : // before the bind() restrict the permissions as much as possible
249 : // for security reasons (only available under Linux apparently)
250 : //
251 1 : if(fchmod(f_socket.get(), S_IRUSR | S_IWUSR) != 0)
252 : {
253 0 : int const e(errno);
254 0 : SNAP_LOG_ERROR
255 0 : << "fchmod() failed changing permissions before bind() (errno: "
256 : << e
257 : << " -- "
258 0 : << strerror(e)
259 : << ") on socket with address \""
260 0 : << f_address.to_uri()
261 : << "\"."
262 : << SNAP_LOG_SEND;
263 0 : throw runtime_error("could not change socket permissions.");
264 : }
265 : #endif
266 :
267 2 : r = bind(
268 2 : f_socket.get()
269 : , reinterpret_cast<sockaddr const *>(&un)
270 : , sizeof(struct sockaddr_un));
271 :
272 1 : std::string const group(f_address.get_group());
273 1 : if(!group.empty())
274 : {
275 0 : if(snapdev::chownnm(un.sun_path, std::string(), group) != 0)
276 : {
277 0 : int const e(errno);
278 0 : SNAP_LOG_ERROR
279 : << "not able to change group ownership of socket file \""
280 : << un.sun_path
281 0 : << "\" (errno: "
282 : << e
283 : << " -- "
284 0 : << strerror(e)
285 : << "); cannot listen on address \""
286 0 : << f_address.to_uri()
287 : << "\"."
288 : << SNAP_LOG_SEND;
289 0 : throw runtime_error("could not change group ownership on socket file.");
290 : }
291 : }
292 :
293 : // after the bind() we can then set the full permissions the way the
294 : // user wants them to be (also bind() applies the umask() so doing
295 : // that with the fchmod() above is likely to fail in many cases).
296 : //
297 : // note: we know that the path in un.snn_path is null terminated.
298 : //
299 1 : if(r == 0
300 1 : && chmod(un.sun_path, f_address.get_mode()) != 0)
301 : {
302 0 : int const e(errno);
303 0 : SNAP_LOG_ERROR
304 0 : << "chmod() failed changing permissions after bind() (errno: "
305 : << e
306 : << " -- "
307 0 : << strerror(e)
308 : << ") on socket with address \""
309 0 : << f_address.to_uri()
310 : << "\"."
311 : << SNAP_LOG_SEND;
312 0 : throw runtime_error("could not change socket permissions.");
313 : }
314 1 : }
315 : else
316 : {
317 : // we want to limit the size because otherwise it would include
318 : // the '\0's after the specified name
319 : //
320 0 : std::size_t const size(sizeof(un.sun_family)
321 : + 1 // for the '\0' in sun_path[0]
322 0 : + strlen(un.sun_path + 1));
323 0 : r = bind(
324 0 : f_socket.get()
325 : , reinterpret_cast<sockaddr const *>(&un)
326 : , size);
327 : }
328 :
329 1 : if(r < 0)
330 : {
331 0 : throw runtime_error(
332 : "could not bind the socket to \""
333 0 : + f_address.to_uri()
334 0 : + "\"");
335 : }
336 :
337 : // start listening, we expect the caller to then call accept() to
338 : // acquire connections
339 : //
340 1 : if(listen(f_socket.get(), f_max_connections) < 0)
341 : {
342 0 : throw runtime_error(
343 : "could not listen to the socket bound to \""
344 0 : + f_address.to_uri()
345 0 : + "\"");
346 : }
347 0 : }
348 :
349 :
350 : /** \brief Clean up the server socket.
351 : *
352 : * This function deletes the socket file if this service used such a socket.
353 : *
354 : * \note
355 : * If the server crashes, that delete may not happen. In order to allow
356 : * for a restart, calling the constructor and setting the force_reuse_addr
357 : * to true is what will generally work best.
358 : */
359 1 : local_stream_server_connection::~local_stream_server_connection()
360 : {
361 1 : f_address.unlink();
362 1 : }
363 :
364 :
365 : /** \brief Check whether this connection is a listener.
366 : *
367 : * This function already returns true since a server is a listener.
368 : * This allows us to have our process_accept() function called instead
369 : * of the process_read().
370 : *
371 : * \return Always true since this is a listening server.
372 : */
373 10 : bool local_stream_server_connection::is_listener() const
374 : {
375 10 : return true;
376 : }
377 :
378 :
379 : /** \brief Retrieve the socket descriptor.
380 : *
381 : * This function returns the socket descriptor. It can be used to
382 : * tweak things on the socket such as making it non-blocking or
383 : * directly accessing the data.
384 : *
385 : * \return The socket descriptor.
386 : */
387 19 : int local_stream_server_connection::get_socket() const
388 : {
389 19 : return f_socket.get();
390 : }
391 :
392 :
393 : /** \brief Retrieve the maximum number of connections.
394 : *
395 : * This function returns the maximum number of connections that can
396 : * be accepted by the socket. This was set by the constructor and
397 : * it cannot be changed later.
398 : *
399 : * \return The maximum number of incoming connections.
400 : */
401 0 : int local_stream_server_connection::get_max_connections() const
402 : {
403 0 : return f_max_connections;
404 : }
405 :
406 :
407 : /** \brief Retrieve one new connection.
408 : *
409 : * This function will wait until a new connection arrives and returns a
410 : * new bio_client object for each new connection.
411 : *
412 : * If the socket is made non-blocking then the function may return without
413 : * a bio_client object (i.e. a null pointer instead.)
414 : *
415 : * \return A file descriptor representing the new connection socket.
416 : */
417 1 : snapdev::raii_fd_t local_stream_server_connection::accept()
418 : {
419 1 : struct sockaddr_un un;
420 1 : socklen_t len(sizeof(un));
421 : snapdev::raii_fd_t r(::accept(
422 2 : f_socket.get()
423 : , reinterpret_cast<sockaddr *>(&un)
424 2 : , &len));
425 1 : if(r == nullptr)
426 : {
427 0 : throw runtime_error("failed accepting a new AF_UNIX client");
428 : }
429 :
430 : // force a close on execve() to avoid sharing the socket in child
431 : // processes
432 : //
433 1 : if(f_close_on_exec)
434 : {
435 : // if this call fails, we ignore the error, but still log the event
436 : //
437 1 : if(fcntl(r.get(), F_SETFD, FD_CLOEXEC) != 0)
438 : {
439 0 : SNAP_LOG_WARNING
440 : << "::accept(): an error occurred trying"
441 : " to mark accepted AF_UNIX socket with FD_CLOEXEC."
442 : << SNAP_LOG_SEND;
443 : }
444 : }
445 :
446 2 : return r;
447 0 : }
448 :
449 :
450 : /** \brief Return the current state of the close-on-exec flag.
451 : *
452 : * This function returns the current state of the close-on-exec flag. This
453 : * is the flag as defined in the contructor or by the set_close_on_exec()
454 : * function. It does not represent the status of the server socket nor
455 : * of the clients that were accept()'ed by this class.
456 : *
457 : * It will, however, be used whenever the accept() is called in the future.
458 : *
459 : * \return The current status of the close-on-exec flag.
460 : */
461 0 : bool local_stream_server_connection::get_close_on_exec() const
462 : {
463 0 : return f_close_on_exec;
464 : }
465 :
466 :
467 : /** \brief Change the close-on-exec flag for future accept() calls.
468 : *
469 : * This function allows you to change the close-on-exec flag after
470 : * you created a Unix server. This means you may say that the server
471 : * needs to be closed, but not the connections or vice versa.
472 : *
473 : * \param[in] yes Whether the close-on-exec will be set on sockets
474 : * returned by the accept() function.
475 : */
476 0 : void local_stream_server_connection::set_close_on_exec(bool yes)
477 : {
478 0 : f_close_on_exec = yes;
479 0 : }
480 :
481 :
482 : /** \brief Retrieve the server IP address.
483 : *
484 : * This function returns the IP address used to bind the socket. This
485 : * is the address clients have to use to connect to the server unless
486 : * the address was set to all zeroes (0.0.0.0) in which case any user
487 : * can connect.
488 : *
489 : * The IP address cannot be changed.
490 : *
491 : * \return The server IP address.
492 : */
493 0 : addr::addr_unix local_stream_server_connection::get_addr() const
494 : {
495 0 : return f_address;
496 : }
497 :
498 :
499 :
500 :
501 :
502 : } // namespace ed
503 : // vim: ts=4 sw=4 et
|