Skip to content
Snippets Groups Projects
Commit 9cf03555 authored by Vladimir Davydov's avatar Vladimir Davydov Committed by Vladimir Davydov
Browse files

iostream: shutdown socket fd before close

If a socket fd is shared by a child process, closing it in the parent
will not shut down the underlying connection. As a result, the server
may hang executing the graceful shutdown protocol. Fix this problem by
explicitly shutting down the connection socket fd before closing it.

This is a recommended way to terminate a Unix socket connection, see
http://www.faqs.org/faqs/unix-faq/socket/#:~:text=2.6.%20%20When%20should%20I%20use%20shutdown()%3F

Closes #7256

NO_DOC=bug fix
parent 4bf52367
No related branches found
No related tags found
No related merge requests found
## bugfix/core
* Fixed a bug because of which a net.box connection was not properly terminated
when the process had a child (for example, started with `popen`) sharing the
connection socket fd. The bug could lead to a server hanging at exit while
executing the graceful shutdown protocol (gh-7256).
......@@ -9,6 +9,7 @@
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
......@@ -33,6 +34,12 @@ iostream_close(struct iostream *io)
{
int fd = io->fd;
iostream_destroy(io);
/*
* Explicitly shut down the socket before closing its fd so that
* the connection will be terminated even if the Tarantool process
* forked and the child process did not close parent fds.
*/
shutdown(fd, SHUT_RDWR);
close(fd);
}
......
local fiber = require('fiber')
local net = require('net.box')
local popen = require('popen')
local server = require('test.luatest_helpers.server')
local t = require('luatest')
local g = t.group()
......@@ -524,3 +525,17 @@ g.test_net_box_callback_garbage_collected = function()
g.server:stop()
conn:close()
end
-- Checks that the client closes the connection socket fd even if there is
-- a child processes that shares it (gh-6820).
g.test_graceful_shutdown_and_fork = function()
g.server:exec(function()
box.ctl.set_on_shutdown_timeout(9000)
end)
local c = net.connect(g.server.net_box_uri)
local p = popen.new({'/usr/bin/sleep', '9000'}, {close_fds = false})
t.assert(p)
c:close()
g.server:stop()
p:close()
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment