Commit 47d837a4 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Make named a singleton process [RT#37908]

Conflicts:
	bin/tests/system/conf.sh.in
	lib/dns/win32/libdns.def.in
	lib/isc/win32/file.c

The merge also needed to update files in legacy and tcp system tests
(newly introduced in master after branch was created) to introduce use
of lockfile.
parent c60ee6ed
4022. [func] Stop multiple spawns of named by limiting number of
processes to 1. This is done by using a lockfile and
checking whether we can listen on any configured
TCP interfaces. [RT #37908]
4021. [bug] Adjust max-recursion-queries to accommodate
the need for more queries when the cache is
empty. [RT #38104]
......
......@@ -133,6 +133,9 @@ EXTERN const char * ns_g_logfile INIT(NULL);
EXTERN const char * ns_g_defaultsessionkeyfile
INIT(NS_LOCALSTATEDIR "/run/named/"
"session.key");
EXTERN const char * ns_g_singletonfile INIT(NS_LOCALSTATEDIR
"/run/named/"
"named.lock");
#if NS_RUN_PID_DIR
EXTERN const char * ns_g_defaultpidfile INIT(NS_LOCALSTATEDIR
......
......@@ -115,7 +115,14 @@ ns_interfacemgr_detach(ns_interfacemgr_t **targetp);
void
ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr);
void
isc_boolean_t
ns_interfacemgr_islistening(ns_interfacemgr_t *mgr);
/*%
* Return if the manager is listening on any interface. It can be called
* after a scan or adjust.
*/
isc_result_t
ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose);
/*%
* Scan the operatings system's list of network interfaces
......@@ -127,7 +134,7 @@ ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose);
* in named.conf.
*/
void
isc_result_t
ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
isc_boolean_t verbose);
/*%
......
......@@ -545,17 +545,19 @@ ns_interface_accepttcp(ns_interface_t *ifp) {
tcp_bind_failure:
isc_socket_detach(&ifp->tcpsocket);
tcp_socket_failure:
return (ISC_R_SUCCESS);
return (result);
}
static isc_result_t
ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
const char *name, ns_interface_t **ifpret,
isc_boolean_t accept_tcp, isc_dscp_t dscp)
isc_boolean_t accept_tcp, isc_dscp_t dscp,
isc_boolean_t *tcp_addr_in_use)
{
isc_result_t result;
ns_interface_t *ifp = NULL;
REQUIRE(ifpret != NULL && *ifpret == NULL);
REQUIRE(tcp_addr_in_use == NULL || *tcp_addr_in_use == ISC_FALSE);
result = ns_interface_create(mgr, addr, name, &ifp);
if (result != ISC_R_SUCCESS)
......@@ -570,6 +572,10 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
if (!ns_g_notcp && accept_tcp == ISC_TRUE) {
result = ns_interface_accepttcp(ifp);
if (result != ISC_R_SUCCESS) {
if ((result == ISC_R_ADDRINUSE) &&
(tcp_addr_in_use != NULL))
*tcp_addr_in_use = ISC_TRUE;
/*
* XXXRTH We don't currently have a way to easily stop
* dispatch service, so we currently return
......@@ -807,6 +813,8 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
isc_boolean_t log_explicit = ISC_FALSE;
isc_boolean_t dolistenon;
char sabuf[ISC_SOCKADDR_FORMATSIZE];
isc_boolean_t tried_listening;
isc_boolean_t all_addresses_in_use;
if (ext_listen != NULL)
adjusting = ISC_TRUE;
......@@ -883,7 +891,8 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
result = ns_interface_setup(mgr, &listen_addr,
"<any>", &ifp,
ISC_TRUE,
le->dscp);
le->dscp,
NULL);
if (result == ISC_R_SUCCESS)
ifp->flags |= NS_INTERFACEFLAG_ANYADDR;
else
......@@ -913,6 +922,8 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
clearlistenon(mgr);
}
tried_listening = ISC_FALSE;
all_addresses_in_use = ISC_TRUE;
for (result = isc_interfaceiter_first(iter);
result == ISC_R_SUCCESS;
result = isc_interfaceiter_next(iter))
......@@ -1049,6 +1060,8 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
sabuf, ifp->dscp);
}
} else {
isc_boolean_t tcp_addr_in_use = ISC_FALSE;
if (adjusting == ISC_FALSE &&
ipv6_wildcard == ISC_TRUE)
continue;
......@@ -1083,7 +1096,12 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
&ifp,
(adjusting == ISC_TRUE) ?
ISC_FALSE : ISC_TRUE,
le->dscp);
le->dscp,
&tcp_addr_in_use);
tried_listening = ISC_TRUE;
if (!tcp_addr_in_use)
all_addresses_in_use = ISC_FALSE;
if (result != ISC_R_SUCCESS) {
isc_log_write(IFMGR_COMMON_LOGARGS,
......@@ -1114,23 +1132,26 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
"interface iteration failed: %s",
isc_result_totext(result));
else
result = ISC_R_SUCCESS;
result = ((tried_listening && all_addresses_in_use) ?
ISC_R_ADDRINUSE : ISC_R_SUCCESS);
cleanup_iter:
isc_interfaceiter_destroy(&iter);
return (result);
}
static void
static isc_result_t
ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
isc_boolean_t verbose)
{
isc_result_t result;
isc_boolean_t purge = ISC_TRUE;
REQUIRE(NS_INTERFACEMGR_VALID(mgr));
mgr->generation++; /* Increment the generation count. */
if (do_scan(mgr, ext_listen, verbose) != ISC_R_SUCCESS)
result = do_scan(mgr, ext_listen, verbose);
if ((result != ISC_R_SUCCESS) && (result != ISC_R_ADDRINUSE))
purge = ISC_FALSE;
/*
......@@ -1152,18 +1173,27 @@ ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
"not listening on any interfaces");
}
return (result);
}
void
isc_boolean_t
ns_interfacemgr_islistening(ns_interfacemgr_t *mgr) {
REQUIRE(NS_INTERFACEMGR_VALID(mgr));
return (ISC_LIST_EMPTY(mgr->interfaces) ? ISC_FALSE : ISC_TRUE);
}
isc_result_t
ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) {
ns_interfacemgr_scan0(mgr, NULL, verbose);
return (ns_interfacemgr_scan0(mgr, NULL, verbose));
}
void
isc_result_t
ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
isc_boolean_t verbose)
{
ns_interfacemgr_scan0(mgr, list, verbose);
return (ns_interfacemgr_scan0(mgr, list, verbose));
}
void
......
......@@ -424,7 +424,7 @@ parse_command_line(int argc, char *argv[]) {
save_command_line(argc, argv);
/* PLEASE keep options synchronized when main is hooked! */
#define CMDLINE_FLAGS "46c:C:d:D:E:fFgi:lL:m:n:N:p:P:sS:t:T:U:u:vVx:"
#define CMDLINE_FLAGS "46c:C:d:D:E:fFgi:lL:m:n:N:p:P:sS:t:T:U:u:vVx:X:"
isc_commandline_errprint = ISC_FALSE;
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
switch (ch) {
......@@ -613,6 +613,12 @@ parse_command_line(int argc, char *argv[]) {
LIBXML_DOTTED_VERSION);
#endif
exit(0);
case 'x':
/* Obsolete. No longer in use. Ignore. */
break;
case 'X':
ns_g_singletonfile = isc_commandline_argument;
break;
case 'F':
/* Reserved for FIPS mode */
/* FALLTHROUGH */
......@@ -1215,6 +1221,11 @@ main(int argc, char *argv[]) {
parse_command_line(argc, argv);
if (!ns_os_issingleton(ns_g_singletonfile))
ns_main_earlyfatal("could not lock %s; "
"another named process may be running",
ns_g_singletonfile);
/*
* Warn about common configuration error.
*/
......
......@@ -77,6 +77,7 @@
<arg><option>-u <replaceable class="parameter">user</replaceable></option></arg>
<arg><option>-v</option></arg>
<arg><option>-V</option></arg>
<arg><option>-X <replaceable class="parameter">lock-file</replaceable></option></arg>
<arg><option>-x <replaceable class="parameter">cache-file</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
......@@ -378,6 +379,18 @@
</listitem>
</varlistentry>
<varlistentry>
<term>-X <replaceable class="parameter">lock-file</replaceable></term>
<listitem>
<para>
Acquire a lock on the specified file at runtime; this
helps to prevent duplicate <command>named</command> instances
from running simultaneously. If not specified via this option,
the default lockfile is <filename>/var/run/named/named.lock</filename>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-x <replaceable class="parameter">cache-file</replaceable></term>
<listitem>
......
......@@ -4718,14 +4718,15 @@ directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
return (ISC_R_SUCCESS);
}
static void
static isc_result_t
scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
isc_result_t result;
isc_boolean_t match_mapped = server->aclenv.match_mapped;
#ifdef HAVE_GEOIP
isc_boolean_t use_ecs = server->aclenv.geoip_use_ecs;
#endif
ns_interfacemgr_scan(server->interfacemgr, verbose);
result = ns_interfacemgr_scan(server->interfacemgr, verbose);
/*
* Update the "localhost" and "localnets" ACLs to match the
* current set of network interfaces.
......@@ -4737,6 +4738,8 @@ scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
#ifdef HAVE_GEOIP
server->aclenv.geoip_use_ecs = use_ecs;
#endif
return (result);
}
static isc_result_t
......@@ -5827,7 +5830,20 @@ load_configuration(const char *filename, ns_server_t *server,
* to configure the query source, since the dispatcher we use might
* be shared with an interface.
*/
scan_interfaces(server, ISC_TRUE);
result = scan_interfaces(server, ISC_TRUE);
/*
* Check that named is able to TCP listen on at least one
* interface. Otherwise, another named process could be running
* and we should fail.
*/
if (first_time && (result == ISC_R_ADDRINUSE)) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"unable to listen on any configured interfaces");
result = ISC_R_FAILURE;
goto cleanup;
}
/*
* Arrange for further interface scanning to occur periodically
......
......@@ -57,6 +57,9 @@ ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user);
void
ns_os_writepidfile(const char *filename, isc_boolean_t first_time);
isc_boolean_t
ns_os_issingleton(const char *filename);
void
ns_os_shutdown(void);
......
......@@ -55,6 +55,7 @@
static char *pidfile = NULL;
static int devnullfd = -1;
static int singletonfd = -1;
#ifndef ISC_FACILITY
#define ISC_FACILITY LOG_DAEMON
......@@ -900,10 +901,47 @@ ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
(void)fclose(lockfile);
}
isc_boolean_t
ns_os_issingleton(const char *filename) {
struct flock lock;
if (singletonfd != -1)
return (ISC_TRUE);
/*
* ns_os_openfile() uses safeopen() which removes any existing
* files. We can't use that here.
*/
singletonfd = open(filename, O_WRONLY | O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (singletonfd == -1)
return (ISC_FALSE);
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 1;
/* Non-blocking (does not wait for lock) */
if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
close(singletonfd);
singletonfd = -1;
return (ISC_FALSE);
}
return (ISC_TRUE);
}
void
ns_os_shutdown(void) {
closelog();
cleanup_pidfile();
if (singletonfd != -1) {
close(singletonfd);
singletonfd = -1;
}
}
isc_result_t
......
......@@ -55,6 +55,9 @@ ns_os_openfile(const char *filename, int mode, isc_boolean_t switch_user);
void
ns_os_writepidfile(const char *filename, isc_boolean_t first_time);
isc_boolean_t
ns_os_issingleton(const char *filename);
void
ns_os_shutdown(void);
......
......@@ -49,6 +49,7 @@
static char *pidfile = NULL;
static int devnullfd = -1;
static int singletonfd = -1;
static BOOL Initialized = FALSE;
......@@ -282,10 +283,46 @@ ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
(void)fclose(lockfile);
}
isc_boolean_t
ns_os_issingleton(const char *filename) {
OVERLAPPED o;
if (singletonfd != -1)
return (ISC_TRUE);
/*
* ns_os_openfile() uses safeopen() which removes any existing
* files. We can't use that here.
*/
singletonfd = open(filename, O_WRONLY | O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (singletonfd == -1)
return (ISC_FALSE);
memset(&o, 0, sizeof(o));
/* Expect ERROR_LOCK_VIOLATION if already locked */
if (!LockFileEx((HANDLE) _get_osfhandle(singletonfd),
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0, 0, 1, &o)) {
close(singletonfd);
singletonfd = -1;
return (ISC_FALSE);
}
return (ISC_TRUE);
}
void
ns_os_shutdown(void) {
closelog();
cleanup_pidfile();
if (singletonfd != -1) {
(void) UnlockFile((HANDLE) _get_osfhandle(singletonfd),
0, 0, 0, 1);
close(singletonfd);
singletonfd = -1;
}
ntservice_shutdown(); /* This MUST be the last thing done */
}
......
......@@ -23,3 +23,4 @@
rm -f dig.out.*
rm -f ns2/example.db ns2/tsigzone.db ns2/example.db.jnl ns2/named.conf
rm -f */named.memstats
rm -f ns*/named.lock
......@@ -24,3 +24,4 @@ rm -f dig.out.*
rm -f */named.memstats
rm -f */named.conf
rm -f */named.run
rm -f ns*/named.lock
# this server runs named with only one worker thread
-m record,size,mctx -c named.conf -d 99 -g -T clienttest -n 1
\ No newline at end of file
-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T clienttest -n 1
......@@ -25,3 +25,4 @@ rm -f ns2/core*
rm -f ns2/inline.db.jbk
rm -f ns2/inline.db.signed
rm -f ns2/inlineslave.bk*
rm -f ns*/named.lock
......@@ -23,3 +23,4 @@
rm -f dig.out.*
rm -f ns2/named.conf
rm -f */named.memstats
rm -f ns*/named.lock
......@@ -53,3 +53,4 @@ rm -f ns3/ttl*.db
rm -f signing.out.*
rm -f ns3/*.nzf
rm -f digcomp.out.test*
rm -f ns*/named.lock
......@@ -18,3 +18,4 @@ rm -f ns?/named.run
rm -f ns?/named.memstats
rm -f rndc.status.ns*
rm -f dig.out.ns*
rm -f ns*/named.lock
......@@ -25,3 +25,4 @@ rm -f dig.out.ns2
rm -f dig.out.expire
rm -f */named.memstats
rm -f ns2/named_dump.db
rm -f ns*/named.lock
......@@ -16,3 +16,4 @@
rm -f dig.ns*.test*
rm -f ns2/example.bk
rm -f ns*/named.lock
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment