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

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
......@@ -17,3 +17,4 @@
rm -f good.conf.in good.conf.out badzero.conf *.out
rm -rf test.keydir
rm -f checkconf.out*
rm -f ns*/named.lock
......@@ -17,3 +17,4 @@
# $Id$
rm -f checkds.*
rm -f ns*/named.lock
......@@ -24,3 +24,4 @@ rm -f ns1/*.update.db.jnl
rm -f ns4/*.update.db
rm -f ns4/*.update.db.jnl
rm -f */named.memstats
rm -f ns*/named.lock
......@@ -13,3 +13,4 @@
# PERFORMANCE OF THIS SOFTWARE.
rm -f test.* good1.db.map good1.db.raw named-compilezone
rm -f ns*/named.lock
......@@ -72,9 +72,9 @@ SUBDIRS="acl additional allow_query addzone autosign builtin
legacy limits logfileconfig lwresd masterfile masterformat
metadata notify nslookup nsupdate pending @PKCS11_TEST@
reclimit redirect resolver rndc rpz rrl rrchecker rrsetorder
rsabigexponent sit sfcache smartsign sortlist spf staticstub
statistics stub tcp tkey tsig tsiggss unknown upforwd
verify views wildcard xfer xferquota zero zonechecks"
rsabigexponent runtime sit sfcache smartsign sortlist spf
staticstub statistics stub tcp tkey tsig tsiggss unknown
upforwd verify views wildcard xfer xferquota zero zonechecks"
# Use the CONFIG_SHELL detected by configure for tests
SHELL=@SHELL@
......
......@@ -18,3 +18,4 @@ rm -f named-compilezone
rm -f */K*.key
rm -f */K*.private
rm -rf coverage.*
rm -f ns*/named.lock
......@@ -18,3 +18,4 @@
rm -f ns1/named.conf ns1/named.run ns1/named.memstats
rm -f dig.out.*
rm -f ns*/named.lock
......@@ -17,3 +17,4 @@
rm -f delv.out.test*
rm -f */named.memstats
rm -f */named.run
rm -f ns*/named.lock
......@@ -43,3 +43,4 @@ rm -f ns6/dsset-*
rm -f ns6/signer.err
rm -f */named.memstats
rm -f dig.out.ns*.test*
rm -f ns*/named.lock
......@@ -25,3 +25,4 @@ rm -f ns2/*.mkeys
rm -f ns2/*.mkeys.jnl
rm -f dig.out.ns?.test*
rm -f ns2/named.secroots
rm -f ns*/named.lock
......@@ -31,7 +31,7 @@ then
ret=0
$PERL $SYSTEMTESTTOP/stop.pl . ns2 || ret=1
sleep 1
(cd ns2; $NAMED -g -d 100 -c named.conf >> named.run 2>&1 & )
(cd ns2; $NAMED -g -d 100 -X named.lock -c named.conf >> named.run 2>&1 & )
sleep 2
$DIG $DIGOPTS soa . @10.53.0.2 > dig.out.ns2.test$n || ret=1
grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1
......
......@@ -19,3 +19,4 @@
rm -f dig.out.*
rm -f */named.memstats
rm -f */named.run
rm -f ns*/named.lock
......@@ -22,3 +22,4 @@ rm -f ns1/update.txt
rm -f */named.memstats
rm -f ns1/ddns.key
rm -f dig.out*
rm -f ns*/named.lock
......@@ -21,3 +21,4 @@
#
rm -f */named.memstats
rm -f dig.out.*
rm -f ns*/named.lock
......@@ -19,3 +19,4 @@ rm -f ns1/signed.db*
rm -f ns1/dsset-signed.
rm -f */named.memstats
rm -f dig.out.*
rm -f ns*/named.lock
......@@ -81,3 +81,4 @@ rm -f ns3/dnskey-nsec3-unknown.example.db
rm -f ns3/dnskey-nsec3-unknown.example.db.tmp
rm -f ns3/dnskey-unknown.example.db
rm -f ns3/dnskey-unknown.example.db.tmp
rm -f ns*/named.lock
-m record,size,mctx -c named.conf -d 99 -g -T nonearest -T clienttest
-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T nonearest -T clienttest
......@@ -14,3 +14,4 @@
rm -f */root.bk
rm -f dig.out.10.53.0.?
rm -f ns*/named.lock
-m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T dscp=46