diff --git a/src/box/iproto.cc b/src/box/iproto.cc index d936f68ba997b7263950bc3b057cc7b40fac484b..6c96c483edf2137efbd26df922d420b5dce6d9da 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -1043,6 +1043,8 @@ net_cord_f(va_list /* ap */) * connections. */ fiber_yield(); + if (evio_service_is_active(&binary)) + evio_service_stop(&binary); rmean_delete(rmean_net); return 0; diff --git a/src/evio.cc b/src/evio.cc index 4afc871558876c61a25a03ea564f7a9b9539b113..a407db4711aa185bba4059ea3df4267c314589e7 100644 --- a/src/evio.cc +++ b/src/evio.cc @@ -194,6 +194,32 @@ evio_service_accept_cb(ev_loop * /* loop */, ev_io *watcher, } } +static bool +evio_service_reuse_addr(struct evio_service *service) +{ + if ((service->addr.sa_family != AF_UNIX) || (errno != EADDRINUSE)) + return false; + int save_errno = errno; + int cl_fd = sio_socket(service->addr.sa_family, + SOCK_STREAM, 0); + + if (connect(cl_fd, &service->addr, service->addr_len) == 0) + goto err; + + if (errno != ECONNREFUSED) + goto err; + + if (unlink(((struct sockaddr_un *)(&service->addr))->sun_path)) + goto err; + close(cl_fd); + + return true; +err: + errno = save_errno; + close(cl_fd); + return false; +} + /** Try to bind and listen on the configured port. * * Throws an exception if error. @@ -213,12 +239,18 @@ evio_service_bind_addr(struct evio_service *service) evio_setsockopt_server(fd, service->addr.sa_family, SOCK_STREAM); - if (sio_bind(fd, &service->addr, service->addr_len) || - sio_listen(fd)) { + if (sio_bind(fd, &service->addr, service->addr_len)) { assert(errno == EADDRINUSE); - close(fd); + if (!evio_service_reuse_addr(service) || + sio_bind(fd, &service->addr, service->addr_len)) { + return -1; + } + } + + if (sio_listen(fd)) { return -1; } + say_info("%s: bound to %s", evio_service_name(service), sio_strfaddr(&service->addr, service->addr_len)); diff --git a/test/app/socket.result b/test/app/socket.result index df01facbdb3a3fd210f4aaca39bd2bf9db332b55..4398e836ff5e883273201f03a69d1df11c2fe446 100644 --- a/test/app/socket.result +++ b/test/app/socket.result @@ -263,7 +263,7 @@ s:error() --- - null ... -s:bind('127.0.0.1', 3457) +s:bind('127.0.0.1', 0) --- - true ... @@ -293,7 +293,7 @@ sc:nonblock(false) --- - false ... -sc:sysconnect('127.0.0.1', 3457) +sc:sysconnect('127.0.0.1', s:name().port) --- - true ... @@ -389,7 +389,7 @@ s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true) --- - true ... -s:bind('127.0.0.1', 3457) +s:bind('127.0.0.1', 0) --- - true ... @@ -400,7 +400,7 @@ s:listen(128) sc = socket('PF_INET', 'SOCK_STREAM', 'tcp') --- ... -sc:sysconnect('127.0.0.1', 3457) or errno() == errno.EINPROGRESS +sc:sysconnect('127.0.0.1', s:name().port) or errno() == errno.EINPROGRESS --- - true ... @@ -757,14 +757,14 @@ sc:close() s = socket('AF_INET', 'SOCK_DGRAM', 'udp') --- ... -s:bind('127.0.0.1', 3548) +s:bind('127.0.0.1', 0) --- - true ... sc = socket('AF_INET', 'SOCK_DGRAM', 'udp') --- ... -sc:sendto('127.0.0.1', 3548, 'Hello, world') +sc:sendto('127.0.0.1', s:name().port, 'Hello, world') --- - 12 ... @@ -776,7 +776,7 @@ s:recv() --- - Hello, world ... -sc:sendto('127.0.0.1', 3548, 'Hello, world, 2') +sc:sendto('127.0.0.1', s:name().port, 'Hello, world, 2') --- - 15 ... @@ -914,16 +914,16 @@ socket.tcp_connect('127.0.0.1', 80, 0.00000000001) - null ... -- AF_INET -port = 35490 ---- -... s = socket('AF_INET', 'SOCK_STREAM', 'tcp') --- ... -s:bind('127.0.0.1', port) +s:bind('127.0.0.1', 0) --- - true ... +port = s:name().port +--- +... s:listen() --- - true @@ -1036,9 +1036,6 @@ s = nil --- ... -- close -port = 65454 ---- -... serv = socket('AF_INET', 'SOCK_STREAM', 'tcp') --- ... @@ -1050,6 +1047,9 @@ serv:bind('127.0.0.1', port) --- - true ... +port = serv:name().port +--- +... serv:listen() --- - true diff --git a/test/app/socket.test.lua b/test/app/socket.test.lua index ed7e3105e09ca3de46eaf2b45edff96d7cf1b4b3..75eddfb62e861c48e43798034f4cd12c1ea19c5e 100644 --- a/test/app/socket.test.lua +++ b/test/app/socket.test.lua @@ -86,7 +86,7 @@ s:close() s = socket('PF_INET', 'SOCK_STREAM', 'tcp') s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true) s:error() -s:bind('127.0.0.1', 3457) +s:bind('127.0.0.1', 0) s:error() s:listen(128) sevres = {} @@ -95,7 +95,7 @@ type(require('fiber').create(function() s:readable() do local sc = s:accept() ta sc = socket('PF_INET', 'SOCK_STREAM', 'tcp') sc:nonblock(false) -sc:sysconnect('127.0.0.1', 3457) +sc:sysconnect('127.0.0.1', s:name().port) sc:nonblock(true) sc:readable(.5) sc:sysread() @@ -121,12 +121,12 @@ s:close() s = socket('PF_INET', 'SOCK_STREAM', 'tcp') s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true) -s:bind('127.0.0.1', 3457) +s:bind('127.0.0.1', 0) s:listen(128) sc = socket('PF_INET', 'SOCK_STREAM', 'tcp') -sc:sysconnect('127.0.0.1', 3457) or errno() == errno.EINPROGRESS +sc:sysconnect('127.0.0.1', s:name().port) or errno() == errno.EINPROGRESS sc:writable(10) sc:write('Hello, world') @@ -243,13 +243,13 @@ sc:nonblock(true) sc:close() s = socket('AF_INET', 'SOCK_DGRAM', 'udp') -s:bind('127.0.0.1', 3548) +s:bind('127.0.0.1', 0) sc = socket('AF_INET', 'SOCK_DGRAM', 'udp') -sc:sendto('127.0.0.1', 3548, 'Hello, world') +sc:sendto('127.0.0.1', s:name().port, 'Hello, world') s:readable(10) s:recv() -sc:sendto('127.0.0.1', 3548, 'Hello, world, 2') +sc:sendto('127.0.0.1', s:name().port, 'Hello, world, 2') s:readable(10) d, from = s:recvfrom() from.port > 0 @@ -292,9 +292,9 @@ s:close() socket.tcp_connect('127.0.0.1', 80, 0.00000000001) -- AF_INET -port = 35490 s = socket('AF_INET', 'SOCK_STREAM', 'tcp') -s:bind('127.0.0.1', port) +s:bind('127.0.0.1', 0) +port = s:name().port s:listen() sc, e = socket.tcp_connect('127.0.0.1', port), errno() sc ~= nil @@ -329,10 +329,10 @@ s:writable(0) s = nil -- close -port = 65454 serv = socket('AF_INET', 'SOCK_STREAM', 'tcp') serv:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true) serv:bind('127.0.0.1', port) +port = serv:name().port serv:listen() test_run:cmd("setopt delimiter ';'") f = fiber.create(function(serv) diff --git a/test/box-tap/cfg.result b/test/box-tap/cfg.result index 0aeaea69b8a9bbb5ce614e89b4f5e0c58b948ad0..f83baff72e3a6f2ac96b0c363b39267d81102e80 100644 --- a/test/box-tap/cfg.result +++ b/test/box-tap/cfg.result @@ -1,5 +1,5 @@ TAP version 13 -1..34 +1..36 ok - box is not started ok - invalid replication_source ok - invalid wal_mode @@ -34,3 +34,5 @@ ok - logger_nonblock default value ok - logger_nonblock new value ok - dynamic listen ok - dynamic listen +ok - reuse unix socket +ok - delete socket at exit diff --git a/test/box-tap/cfg.test.lua b/test/box-tap/cfg.test.lua index 67ae04d540ca97a8a4dfa2a0f5fc79e21261dbbb..b6be775ed0ffb621219148861035f5a9ac61c025 100755 --- a/test/box-tap/cfg.test.lua +++ b/test/box-tap/cfg.test.lua @@ -4,7 +4,7 @@ local tap = require('tap') local test = tap.test('cfg') local socket = require('socket') local fio = require('fio') -test:plan(34) +test:plan(36) -------------------------------------------------------------------------------- -- Invalid values @@ -154,7 +154,7 @@ test:is(run_script(code), 0, "logger_nonblock new value") local path = './tarantool.sock' os.remove(path) box.cfg{ listen = 'unix/:'..path } -s = socket.tcp_connect('unix/', path) +local s = socket.tcp_connect('unix/', path) test:isnt(s, nil, "dynamic listen") if s then s:close() end box.cfg{ listen = '' } @@ -163,5 +163,21 @@ test:isnil(s, 'dynamic listen') if s then s:close() end os.remove(path) +path = './tarantool.sock' +local path2 = './tarantool2.sock' +local s = socket.tcp_server('unix/', path, function () end) +os.execute('ln ' .. path .. ' ' .. path2) +s:close() +box.cfg{ listen = 'unix/:'.. path2} +s = socket.tcp_connect('unix/', path2) +test:isnt(s, nil, "reuse unix socket") +if s then s:close() end +box.cfg{ listen = '' } +os.remove(path2) + +code = " box.cfg{ listen='unix/:'" .. path .. "' } " +run_script(code) +test:isnil(fio.stat(path), "delete socket at exit") + test:check() os.exit(0)