mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[master] Servers now create PID files
Merge branch 'trac3769'
This commit is contained in:
@@ -128,6 +128,35 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
|
|||||||
Upon start up the module will load its configuration and begin listening
|
Upon start up the module will load its configuration and begin listening
|
||||||
for NCRs based on that configuration.
|
for NCRs based on that configuration.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
During startup the server will attempt to create a PID file of the
|
||||||
|
form: [localstatedir]/[conf name].kea-dhcp-ddns.pid
|
||||||
|
where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>localstatedir</command>: The value as passed into the
|
||||||
|
build configure script. It defaults defaults to "/usr/local/var". Note
|
||||||
|
that this value may be overridden at run time by setting the environment
|
||||||
|
variable KEA_PIDFILE_DIR. This is intended primarily for testing purposes.
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>conf name</command>: The confguration file name
|
||||||
|
used to start the server, minus all preceding path and file extension.
|
||||||
|
For example, given a pathname of "/usr/local/etc/kea/myconf.txt", the
|
||||||
|
portion used would be "myconf".
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
If the file already exists and contains the PID of a live process,
|
||||||
|
the server will issue a DHCP_DDNS_ALREADY_RUNNING log message and exit. It
|
||||||
|
is possible, though unlikely, that the file is a remnant of a system crash
|
||||||
|
and the process to which the PID belongs is unrelated to Kea. In such a
|
||||||
|
case it would be necessary to manually delete the PID file.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
|
||||||
</section> <!-- end start-stop -->
|
</section> <!-- end start-stop -->
|
||||||
<section id="d2-configuration">
|
<section id="d2-configuration">
|
||||||
<title>Configuring the DHCP-DDNS Server</title>
|
<title>Configuring the DHCP-DDNS Server</title>
|
||||||
|
@@ -106,6 +106,33 @@ strings <userinput>path</userinput>/kea-dhcp4 | sed -n 's/;;;; //p'
|
|||||||
access. Make sure you run this daemon as root.
|
access. Make sure you run this daemon as root.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
During startup the server will attempt to create a PID file of the
|
||||||
|
form: [localstatedir]/[conf name].kea-dhcp4.pid
|
||||||
|
where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>localstatedir</command>: The value as passed into the
|
||||||
|
build configure script. It defaults defaults to "/usr/local/var". Note
|
||||||
|
that this value may be overridden at run time by setting the environment
|
||||||
|
variable KEA_PIDFILE_DIR. This is intended primarily for testing purposes.
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>conf name</command>: The confguration file name
|
||||||
|
used to start the server, minus all preceding path and file extension.
|
||||||
|
For example, given a pathname of "/usr/local/etc/kea/myconf.txt", the
|
||||||
|
portion used would be "myconf".
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
If the file already exists and contains the PID of a live process,
|
||||||
|
the server will issue a DHCP4_ALREADY_RUNNING log message and exit. It
|
||||||
|
is possible, though unlikely, that the file is a remnant of a system crash
|
||||||
|
and the process to which the PID belongs is unrelated to Kea. In such a
|
||||||
|
case it would be necessary to manually delete the PID file.
|
||||||
|
</para>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="dhcp4-configuration">
|
<section id="dhcp4-configuration">
|
||||||
|
@@ -104,6 +104,32 @@ strings <userinput>path</userinput>/kea-dhcp6 | sed -n 's/;;;; //p'
|
|||||||
access. Make sure you run this daemon as root.
|
access. Make sure you run this daemon as root.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
During startup the server will attempt to create a PID file of the
|
||||||
|
form: [localstatedir]/[conf name].kea-dhcp6.pid
|
||||||
|
where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>localstatedir</command>: The value as passed into the
|
||||||
|
build configure script. It defaults defaults to "/usr/local/var". Note
|
||||||
|
that this value may be overridden at run time by setting the environment
|
||||||
|
variable KEA_PIDFILE_DIR. This is intended primarily for testing purposes.
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<simpara><command>conf name</command>: The confguration file name
|
||||||
|
used to start the server, minus all preceding path and file extension.
|
||||||
|
For example, given a pathname of "/usr/local/etc/kea/myconf.txt", the
|
||||||
|
portion used would be "myconf".
|
||||||
|
</simpara>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
If the file already exists and contains the PID of a live process,
|
||||||
|
the server will issue a DHCP6_ALREADY_RUNNING log message and exit. It
|
||||||
|
is possible, though unlikely, that the file is a remnant of a system crash
|
||||||
|
and the process to which the PID belongs is unrelated to Kea. In such a
|
||||||
|
case it would be necessary to manually delete the PID file.
|
||||||
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="dhcp6-configuration">
|
<section id="dhcp6-configuration">
|
||||||
|
@@ -100,6 +100,16 @@ documented in preceding log entries.
|
|||||||
This is an informational message issued after DHCP_DDNS has submitted DNS
|
This is an informational message issued after DHCP_DDNS has submitted DNS
|
||||||
mapping additions which were received and accepted by an appropriate DNS server.
|
mapping additions which were received and accepted by an appropriate DNS server.
|
||||||
|
|
||||||
|
% DHCP_DDNS_ALREADY_RUNNING %1 already running? %2
|
||||||
|
This is an error message that occurs when DHCP_DDNS encounters a pre-existing
|
||||||
|
PID file which contains the PID of a running process. This most likely
|
||||||
|
indicates an attempt to start a second instance of DHCP_DDNS using the
|
||||||
|
same configuration file. It is possible, though unlikely, that the PID file
|
||||||
|
is a remnant left behind by a server crash or power failure and the PID
|
||||||
|
it contains refers to a process other than DHCP_DDNS. In such an event,
|
||||||
|
it would be necessary to manually remove the PID file. The first argument is
|
||||||
|
the DHCP_DDNS process name, the second contains the PID and PID file.
|
||||||
|
|
||||||
% DHCP_DDNS_AT_MAX_TRANSACTIONS application has %1 queued requests but has reached maximum number of %2 concurrent transactions
|
% DHCP_DDNS_AT_MAX_TRANSACTIONS application has %1 queued requests but has reached maximum number of %2 concurrent transactions
|
||||||
This is a debug message that indicates that the application has DHCP_DDNS
|
This is a debug message that indicates that the application has DHCP_DDNS
|
||||||
requests in the queue but is working as many concurrent requests as allowed.
|
requests in the queue but is working as many concurrent requests as allowed.
|
||||||
@@ -277,6 +287,15 @@ no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS
|
|||||||
configuration needs to be updated or the source of the FQDN itself should be
|
configuration needs to be updated or the source of the FQDN itself should be
|
||||||
investigated.
|
investigated.
|
||||||
|
|
||||||
|
% DHCP_DDNS_PID_FILE_ERROR %1 could not create a PID file: %2
|
||||||
|
This is an error message that occurs when DHCP_DDNS is unable to create
|
||||||
|
its PID file. The log message should contain details sufficient to
|
||||||
|
determine the underlying cause. The most likely culprits are that
|
||||||
|
some portion of the pathname does not exist or a permissions issue. The
|
||||||
|
default path is determined by --localstatedir configure paramter but
|
||||||
|
may be overridden by setting environment variable, KEA_PIDFILE_DIR. The
|
||||||
|
first argument is the DHCP_DDNS process name.
|
||||||
|
|
||||||
% DHCP_DDNS_PROCESS_INIT application init invoked
|
% DHCP_DDNS_PROCESS_INIT application init invoked
|
||||||
This is a debug message issued when the DHCP-DDNS application enters
|
This is a debug message issued when the DHCP-DDNS application enters
|
||||||
its initialization method.
|
its initialization method.
|
||||||
|
@@ -70,6 +70,8 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
|
|||||||
throw; // rethrow it
|
throw; // rethrow it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setProcName(bin_name_);
|
||||||
|
|
||||||
// It is important that we set a default logger name because this name
|
// It is important that we set a default logger name because this name
|
||||||
// will be used when the user doesn't provide the logging configuration
|
// will be used when the user doesn't provide the logging configuration
|
||||||
// in the Kea configuration file.
|
// in the Kea configuration file.
|
||||||
@@ -87,6 +89,18 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
|
|||||||
Daemon::loggerInit(bin_name_.c_str(), verbose_);
|
Daemon::loggerInit(bin_name_.c_str(), verbose_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
createPIDFile();
|
||||||
|
} catch (const dhcp::DaemonPIDExists& ex) {
|
||||||
|
LOG_FATAL(dctl_logger, DHCP_DDNS_ALREADY_RUNNING)
|
||||||
|
.arg(bin_name_).arg(ex.what());
|
||||||
|
isc_throw (LaunchError, "Launch Failed: " << ex.what());
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
LOG_FATAL(dctl_logger, DHCP_DDNS_PID_FILE_ERROR)
|
||||||
|
.arg(app_name_).arg(ex.what());
|
||||||
|
isc_throw (LaunchError, "Launch failed: " << ex.what());
|
||||||
|
}
|
||||||
|
|
||||||
// Log the starting of the service. Although this is the controller
|
// Log the starting of the service. Although this is the controller
|
||||||
// module, use a "DHCP_DDNS_" prefix to the module (to conform to the
|
// module, use a "DHCP_DDNS_" prefix to the module (to conform to the
|
||||||
// principle of least astonishment).
|
// principle of least astonishment).
|
||||||
@@ -173,7 +187,7 @@ DControllerBase::parseArgs(int argc, char* argv[])
|
|||||||
isc_throw(InvalidUsage, "configuration file name missing");
|
isc_throw(InvalidUsage, "configuration file name missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
Daemon::init(optarg);
|
setConfigFile(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?': {
|
case '?': {
|
||||||
|
@@ -50,6 +50,12 @@ public:
|
|||||||
isc::Exception(file, line, what) { };
|
isc::Exception(file, line, what) { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Exception thrown when the controller launch fails.
|
||||||
|
class LaunchError: public isc::Exception {
|
||||||
|
public:
|
||||||
|
LaunchError (const char* file, size_t line, const char* what) :
|
||||||
|
isc::Exception(file, line, what) { };
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Exception thrown when the application process fails.
|
/// @brief Exception thrown when the application process fails.
|
||||||
class ProcessInitError: public isc::Exception {
|
class ProcessInitError: public isc::Exception {
|
||||||
|
@@ -13,6 +13,7 @@ check-local:
|
|||||||
for shtest in $(SHTESTS) ; do \
|
for shtest in $(SHTESTS) ; do \
|
||||||
echo Running test: $$shtest ; \
|
echo Running test: $$shtest ; \
|
||||||
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
||||||
|
export KEA_PIDFILE_DIR=$(abs_top_builddir); \
|
||||||
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
# Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -235,6 +235,7 @@ shutdown_test() {
|
|||||||
test_finish 0
|
test_finish 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server_pid_file_test "${CONFIG}" DHCP_DDNS_ALREADY_RUNNING
|
||||||
dynamic_reconfiguration_test
|
dynamic_reconfiguration_test
|
||||||
shutdown_test "dhcp-ddns.sigterm_test" 15
|
shutdown_test "dhcp-ddns.sigterm_test" 15
|
||||||
shutdown_test "dhcp-ddns.sigint_test" 2
|
shutdown_test "dhcp-ddns.sigint_test" 2
|
||||||
|
@@ -25,6 +25,9 @@ main(int argc, char* argv[]) {
|
|||||||
// src/lib/log/README for info on how to tweak logging
|
// src/lib/log/README for info on how to tweak logging
|
||||||
isc::log::initLogger();
|
isc::log::initLogger();
|
||||||
|
|
||||||
|
// Override --localstatedir value for PID files
|
||||||
|
setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1);
|
||||||
|
|
||||||
int result = RUN_ALL_TESTS();
|
int result = RUN_ALL_TESTS();
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
|
@@ -229,7 +229,7 @@ TEST_F(DStubControllerTest, missingConfigFileArgument) {
|
|||||||
int argc = 2;
|
int argc = 2;
|
||||||
|
|
||||||
// Record start time, and invoke launch().
|
// Record start time, and invoke launch().
|
||||||
EXPECT_THROW(launch(argc, argv), ProcessInitError);
|
EXPECT_THROW(launch(argc, argv), LaunchError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Tests launch with an operational error during application execution.
|
/// @brief Tests launch with an operational error during application execution.
|
||||||
|
@@ -19,6 +19,17 @@ This message is printed when DHCPv4 server enabled an interface to be used
|
|||||||
to receive DHCPv4 traffic. IPv4 socket on this interface will be opened once
|
to receive DHCPv4 traffic. IPv4 socket on this interface will be opened once
|
||||||
Interface Manager starts up procedure of opening sockets.
|
Interface Manager starts up procedure of opening sockets.
|
||||||
|
|
||||||
|
% DHCP4_ALREADY_RUNNING %1 already running? %2
|
||||||
|
This is an error message that occurs when the DHCPv4 server encounters
|
||||||
|
a pre-existing PID file which contains the PID of a running process.
|
||||||
|
This most likely indicates an attempt to start a second instance of
|
||||||
|
the server using the same configuration file. It is possible, though
|
||||||
|
unlikely that the PID file is a remnant left behind by a server crash or
|
||||||
|
power failure and the PID it contains refers to a process other than
|
||||||
|
the server. In such an event, it would be necessary to manually remove
|
||||||
|
the PID file. The first argument is the DHCPv4 process name, the
|
||||||
|
second contains the PID and PID file.
|
||||||
|
|
||||||
% DHCP4_BUFFER_RECEIVED received buffer from %1:%2 to %3:%4 over interface %5
|
% DHCP4_BUFFER_RECEIVED received buffer from %1:%2 to %3:%4 over interface %5
|
||||||
This debug message is logged when the server has received a packet
|
This debug message is logged when the server has received a packet
|
||||||
over the socket. When the message is logged the contents of the received
|
over the socket. When the message is logged the contents of the received
|
||||||
|
@@ -168,9 +168,6 @@ namespace dhcp {
|
|||||||
|
|
||||||
void
|
void
|
||||||
ControlledDhcpv4Srv::init(const std::string& file_name) {
|
ControlledDhcpv4Srv::init(const std::string& file_name) {
|
||||||
// Call parent class's init to initialize file name.
|
|
||||||
Daemon::init(file_name);
|
|
||||||
|
|
||||||
// Configure the server using JSON file.
|
// Configure the server using JSON file.
|
||||||
configure(file_name);
|
configure(file_name);
|
||||||
|
|
||||||
|
@@ -118,6 +118,7 @@ main(int argc, char* argv[]) {
|
|||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Configuration file is required.
|
// Configuration file is required.
|
||||||
if (config_file.empty()) {
|
if (config_file.empty()) {
|
||||||
cerr << "Configuration file not specified." << endl;
|
cerr << "Configuration file not specified." << endl;
|
||||||
@@ -125,7 +126,6 @@ main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ret = EXIT_SUCCESS;
|
int ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// It is important that we set a default logger name because this name
|
// It is important that we set a default logger name because this name
|
||||||
// will be used when the user doesn't provide the logging configuration
|
// will be used when the user doesn't provide the logging configuration
|
||||||
@@ -145,6 +145,11 @@ main(int argc, char* argv[]) {
|
|||||||
// Remember verbose-mode
|
// Remember verbose-mode
|
||||||
server.setVerbose(verbose_mode);
|
server.setVerbose(verbose_mode);
|
||||||
|
|
||||||
|
// Create our PID file.
|
||||||
|
server.setProcName(DHCP4_NAME);
|
||||||
|
server.setConfigFile(config_file);
|
||||||
|
server.createPIDFile();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize the server.
|
// Initialize the server.
|
||||||
server.init(config_file);
|
server.init(config_file);
|
||||||
@@ -173,8 +178,18 @@ main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);
|
LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);
|
||||||
|
|
||||||
} catch (const std::exception& ex) {
|
} catch (const isc::dhcp::DaemonPIDExists& ex) {
|
||||||
|
// First, we print the error on stderr (that should always work)
|
||||||
|
cerr << DHCP4_NAME << " already running? " << ex.what()
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
// Let's also try to log it using logging system, but we're not
|
||||||
|
// sure if it's usable (the exception may have been thrown from
|
||||||
|
// the logger subsystem)
|
||||||
|
LOG_FATAL(dhcp4_logger, DHCP4_ALREADY_RUNNING)
|
||||||
|
.arg(DHCP4_NAME).arg(ex.what());
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
// First, we print the error on stderr (that should always work)
|
// First, we print the error on stderr (that should always work)
|
||||||
cerr << DHCP4_NAME << ": Fatal error during start up: " << ex.what()
|
cerr << DHCP4_NAME << ": Fatal error during start up: " << ex.what()
|
||||||
<< endl;
|
<< endl;
|
||||||
|
@@ -12,6 +12,7 @@ check-local:
|
|||||||
for shtest in $(SHTESTS) ; do \
|
for shtest in $(SHTESTS) ; do \
|
||||||
echo Running test: $$shtest ; \
|
echo Running test: $$shtest ; \
|
||||||
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
||||||
|
export KEA_PIDFILE_DIR=$(abs_top_builddir); \
|
||||||
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@@ -272,6 +272,7 @@ shutdown_test() {
|
|||||||
test_finish 0
|
test_finish 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server_pid_file_test "${CONFIG}" DHCP4_ALREADY_RUNNING
|
||||||
dynamic_reconfiguration_test
|
dynamic_reconfiguration_test
|
||||||
shutdown_test "dhcpv4.sigterm_test" 15
|
shutdown_test "dhcpv4.sigterm_test" 15
|
||||||
shutdown_test "dhcpv4.sigint_test" 2
|
shutdown_test "dhcpv4.sigint_test" 2
|
||||||
|
@@ -25,6 +25,7 @@ main(int argc, char* argv[]) {
|
|||||||
// src/lib/log/README for info on how to tweak logging
|
// src/lib/log/README for info on how to tweak logging
|
||||||
isc::log::initLogger();
|
isc::log::initLogger();
|
||||||
|
|
||||||
|
setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1);
|
||||||
int result = RUN_ALL_TESTS();
|
int result = RUN_ALL_TESTS();
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
|
@@ -19,6 +19,17 @@ This message is printed when DHCPv6 server enabled an interface to be used
|
|||||||
to receive DHCPv6 traffic. IPv6 socket on this interface will be opened once
|
to receive DHCPv6 traffic. IPv6 socket on this interface will be opened once
|
||||||
Interface Manager starts up procedure of opening sockets.
|
Interface Manager starts up procedure of opening sockets.
|
||||||
|
|
||||||
|
% DHCP6_ALREADY_RUNNING %1 already running? %2
|
||||||
|
This is an error message that occurs when the DHCPv6 server encounters
|
||||||
|
a pre-existing PID file which contains the PID of a running process.
|
||||||
|
This most likely indicates an attempt to start a second instance of
|
||||||
|
the server using the same configuration file. It is possible, though
|
||||||
|
unlikely that the PID file is a remnant left behind by a server crash or
|
||||||
|
power failure and the PID it contains refers to a process other than
|
||||||
|
the server. In such an event, it would be necessary to manually remove
|
||||||
|
the PID file. The first argument is the DHCPv6 process name, the second
|
||||||
|
contains the PID and PID file.
|
||||||
|
|
||||||
% DHCP6_ADD_GLOBAL_STATUS_CODE %1: adding Status Code to DHCPv6 packet: %2
|
% DHCP6_ADD_GLOBAL_STATUS_CODE %1: adding Status Code to DHCPv6 packet: %2
|
||||||
This message is logged when the server is adding the top-level
|
This message is logged when the server is adding the top-level
|
||||||
Status Code option. The first argument includes the client and the
|
Status Code option. The first argument includes the client and the
|
||||||
|
@@ -174,9 +174,6 @@ namespace dhcp {
|
|||||||
|
|
||||||
void
|
void
|
||||||
ControlledDhcpv6Srv::init(const std::string& file_name) {
|
ControlledDhcpv6Srv::init(const std::string& file_name) {
|
||||||
// Call parent class's init to initialize file name.
|
|
||||||
Daemon::init(file_name);
|
|
||||||
|
|
||||||
// Configure the server using JSON file.
|
// Configure the server using JSON file.
|
||||||
configure(file_name);
|
configure(file_name);
|
||||||
|
|
||||||
|
@@ -148,6 +148,11 @@ main(int argc, char* argv[]) {
|
|||||||
// Remember verbose-mode
|
// Remember verbose-mode
|
||||||
server.setVerbose(verbose_mode);
|
server.setVerbose(verbose_mode);
|
||||||
|
|
||||||
|
// Create our PID file
|
||||||
|
server.setProcName(DHCP6_NAME);
|
||||||
|
server.setConfigFile(config_file);
|
||||||
|
server.createPIDFile();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize the server, e.g. establish control session
|
// Initialize the server, e.g. establish control session
|
||||||
// Read a configuration file
|
// Read a configuration file
|
||||||
@@ -177,6 +182,17 @@ main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
|
LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
|
||||||
|
|
||||||
|
} catch (const isc::dhcp::DaemonPIDExists& ex) {
|
||||||
|
// First, we print the error on stderr (that should always work)
|
||||||
|
cerr << DHCP6_NAME << " already running? " << ex.what()
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
// Let's also try to log it using logging system, but we're not
|
||||||
|
// sure if it's usable (the exception may have been thrown from
|
||||||
|
// the logger subsystem)
|
||||||
|
LOG_FATAL(dhcp6_logger, DHCP6_ALREADY_RUNNING)
|
||||||
|
.arg(DHCP6_NAME).arg(ex.what());
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
|
|
||||||
// First, we print the error on stderr (that should always work)
|
// First, we print the error on stderr (that should always work)
|
||||||
|
@@ -12,6 +12,7 @@ check-local:
|
|||||||
for shtest in $(SHTESTS) ; do \
|
for shtest in $(SHTESTS) ; do \
|
||||||
echo Running test: $$shtest ; \
|
echo Running test: $$shtest ; \
|
||||||
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
||||||
|
export KEA_PIDFILE_DIR=$(abs_top_builddir); \
|
||||||
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@@ -273,6 +273,7 @@ returned %d."
|
|||||||
test_finish 0
|
test_finish 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server_pid_file_test "${CONFIG}" DHCP6_ALREADY_RUNNING
|
||||||
dynamic_reconfiguration_test
|
dynamic_reconfiguration_test
|
||||||
shutdown_test "dhcpv6.sigterm_test" 15
|
shutdown_test "dhcpv6.sigterm_test" 15
|
||||||
shutdown_test "dhcpv6.sigint_test" 2
|
shutdown_test "dhcpv6.sigint_test" 2
|
||||||
|
@@ -22,6 +22,7 @@ main(int argc, char* argv[]) {
|
|||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
isc::log::initLogger();
|
isc::log::initLogger();
|
||||||
|
|
||||||
|
setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1);
|
||||||
int result = RUN_ALL_TESTS();
|
int result = RUN_ALL_TESTS();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@@ -16,6 +16,7 @@ check-local:
|
|||||||
chmod +x $(abs_builddir)/$$shtest ; \
|
chmod +x $(abs_builddir)/$$shtest ; \
|
||||||
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
|
||||||
export KEACTRL_BUILD_DIR=$(abs_top_builddir); \
|
export KEACTRL_BUILD_DIR=$(abs_top_builddir); \
|
||||||
|
export KEA_PIDFILE_DIR=$(abs_top_builddir); \
|
||||||
export KEACTRL_CONF=$(abs_top_builddir)/src/bin/keactrl/tests/keactrl_test.conf; \
|
export KEACTRL_CONF=$(abs_top_builddir)/src/bin/keactrl/tests/keactrl_test.conf; \
|
||||||
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
${SHELL} $(abs_builddir)/$$shtest || exit ; \
|
||||||
done
|
done
|
||||||
|
@@ -13,14 +13,18 @@
|
|||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <cc/data.h>
|
||||||
#include <dhcpsrv/cfgmgr.h>
|
#include <dhcpsrv/cfgmgr.h>
|
||||||
#include <dhcpsrv/daemon.h>
|
#include <dhcpsrv/daemon.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <cc/data.h>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include <logging.h>
|
|
||||||
#include <log/logger_name.h>
|
#include <log/logger_name.h>
|
||||||
#include <log/logger_support.h>
|
#include <log/logger_support.h>
|
||||||
|
#include <logging.h>
|
||||||
|
#include <util/filename.h>
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
/// @brief provides default implementation for basic daemon operations
|
/// @brief provides default implementation for basic daemon operations
|
||||||
@@ -30,18 +34,22 @@
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
// This is an initial config file location.
|
|
||||||
std::string Daemon::config_file_ = "";
|
|
||||||
|
|
||||||
Daemon::Daemon()
|
Daemon::Daemon()
|
||||||
: signal_set_(), signal_handler_() {
|
: signal_set_(), signal_handler_(), config_file_(""), proc_name_(""),
|
||||||
|
pid_file_dir_(DHCP_DATA_DIR), pid_file_(), am_file_author_(false) {
|
||||||
|
|
||||||
|
// The pid_file_dir can be overridden via environment variable
|
||||||
|
// This is primarily intended to simplify testing
|
||||||
|
const char* const env = getenv("KEA_PIDFILE_DIR");
|
||||||
|
if (env) {
|
||||||
|
pid_file_dir_ = env;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Daemon::~Daemon() {
|
Daemon::~Daemon() {
|
||||||
}
|
if (pid_file_ && am_file_author_) {
|
||||||
|
pid_file_->deleteFile();
|
||||||
void Daemon::init(const std::string& config_file) {
|
}
|
||||||
config_file_ = config_file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Daemon::cleanup() {
|
void Daemon::cleanup() {
|
||||||
@@ -96,5 +104,114 @@ std::string Daemon::getVersion(bool /*extended*/) {
|
|||||||
isc_throw(isc::NotImplemented, "Daemon::getVersion() called");
|
isc_throw(isc::NotImplemented, "Daemon::getVersion() called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Daemon::getConfigFile() const {
|
||||||
|
return (config_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Daemon::setConfigFile(const std::string& config_file) {
|
||||||
|
config_file_ = config_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Daemon::getProcName() const {
|
||||||
|
return (proc_name_);
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
Daemon::setProcName(const std::string& proc_name) {
|
||||||
|
proc_name_ = proc_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Daemon::getPIDFileDir() const {
|
||||||
|
return(pid_file_dir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Daemon::setPIDFileDir(const std::string& pid_file_dir) {
|
||||||
|
pid_file_dir_ = pid_file_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Daemon::getPIDFileName() const {
|
||||||
|
if (pid_file_) {
|
||||||
|
return (pid_file_->getFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ("");
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
Daemon::setPIDFileName(const std::string& pid_file_name) {
|
||||||
|
if (pid_file_) {
|
||||||
|
isc_throw(isc::InvalidOperation, "Daemon::setConfigFile"
|
||||||
|
" file name already set to:" << pid_file_->getFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid_file_name.empty()) {
|
||||||
|
isc_throw(isc::BadValue, "Daemon::setPIDFileName"
|
||||||
|
" file name may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_file_.reset(new util::PIDFile(pid_file_name));
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Daemon::makePIDFileName() const {
|
||||||
|
if (config_file_.empty()) {
|
||||||
|
isc_throw(isc::InvalidOperation,
|
||||||
|
"Daemon::makePIDFileName config file name is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proc_name_.empty()) {
|
||||||
|
isc_throw(isc::InvalidOperation,
|
||||||
|
"Daemon::makePIDFileName process name is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Filename instance from the config_file_ pathname, so we can
|
||||||
|
// extract the fname component.
|
||||||
|
isc::util::Filename file(config_file_);
|
||||||
|
if (file.name().empty()) {
|
||||||
|
isc_throw(isc::BadValue, "Daemon::makePIDFileName config file:"
|
||||||
|
<< config_file_ << " is missing file name");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the pathname for the PID file from the runtime directory,
|
||||||
|
// configuration name and process name.
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << pid_file_dir_ << "/" << file.name()
|
||||||
|
<< "." << proc_name_ << ".pid";
|
||||||
|
|
||||||
|
return(stream.str());
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
Daemon::createPIDFile(int pid) {
|
||||||
|
// If pid_file_ hasn't been instantiated explicitly, then do so
|
||||||
|
// using the default name.
|
||||||
|
if (!pid_file_) {
|
||||||
|
setPIDFileName(makePIDFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we find a pre-existing file containing a live PID we bail.
|
||||||
|
int chk_pid = pid_file_->check();
|
||||||
|
if (chk_pid > 0) {
|
||||||
|
isc_throw(DaemonPIDExists, "Daemon::createPIDFile: PID: " << chk_pid
|
||||||
|
<< " exists, PID file: " << getPIDFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// Write the PID of the current process
|
||||||
|
pid_file_->write();
|
||||||
|
} else {
|
||||||
|
// Write the PID we were given
|
||||||
|
pid_file_->write(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
am_file_author_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <cc/data.h>
|
#include <cc/data.h>
|
||||||
#include <dhcpsrv/srv_config.h>
|
#include <dhcpsrv/srv_config.h>
|
||||||
|
#include <util/pid_file.h>
|
||||||
#include <util/signal_set.h>
|
#include <util/signal_set.h>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -24,6 +25,13 @@
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
|
/// @brief Exception thrown when a the PID file points to a live PID
|
||||||
|
class DaemonPIDExists : public Exception {
|
||||||
|
public:
|
||||||
|
DaemonPIDExists(const char* file, size_t line, const char* what) :
|
||||||
|
isc::Exception(file, line, what) { };
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Base class for all services
|
/// @brief Base class for all services
|
||||||
///
|
///
|
||||||
/// This is the base class that all daemons (DHCPv4, DHCPv6, D2 and possibly
|
/// This is the base class that all daemons (DHCPv4, DHCPv6, D2 and possibly
|
||||||
@@ -38,16 +46,6 @@ namespace dhcp {
|
|||||||
/// Dhcpv6Srv) in tests, without going through the hassles of implemeting stub
|
/// Dhcpv6Srv) in tests, without going through the hassles of implemeting stub
|
||||||
/// methods.
|
/// methods.
|
||||||
///
|
///
|
||||||
/// This class comprises a static object holding a location of the configuration
|
|
||||||
/// file. The object must be static because it is instantiated by the signal
|
|
||||||
/// handler functions, which are static by their nature. The signal handlers
|
|
||||||
/// are used to reconfigure a running server and they need access to the
|
|
||||||
/// configuration file location. They get this access by calling
|
|
||||||
/// @c Daemon::getConfigFile function.
|
|
||||||
///
|
|
||||||
/// By default, the configuration file location is empty and its actual value
|
|
||||||
/// is assigned to the static object in @c Daemon::init function.
|
|
||||||
///
|
|
||||||
/// Classes derived from @c Daemon may install custom signal handlers using
|
/// Classes derived from @c Daemon may install custom signal handlers using
|
||||||
/// @c isc::util::SignalSet class. This base class provides a declaration
|
/// @c isc::util::SignalSet class. This base class provides a declaration
|
||||||
/// of the @c SignalSet object that should be initialized in the derived
|
/// of the @c SignalSet object that should be initialized in the derived
|
||||||
@@ -71,25 +69,6 @@ public:
|
|||||||
/// virtual destructor as well.
|
/// virtual destructor as well.
|
||||||
virtual ~Daemon();
|
virtual ~Daemon();
|
||||||
|
|
||||||
/// @brief Initializes the server.
|
|
||||||
///
|
|
||||||
/// Depending on the configuration backend, it establishes msgq session,
|
|
||||||
/// or reads the configuration file.
|
|
||||||
///
|
|
||||||
/// Note: This function may throw to report enountered problems. It may
|
|
||||||
/// also return false if the initialization was skipped. That may seem
|
|
||||||
/// redundant, but the idea here is that in some cases the configuration
|
|
||||||
/// was read, understood and the decision was made to not start. One
|
|
||||||
/// case where such capability could be needed is when we have a single
|
|
||||||
/// config file for Kea4 and D2, but the DNS Update is disabled. It is
|
|
||||||
/// likely that the D2 will be started, it will analyze its config file,
|
|
||||||
/// decide that it is not needed and will shut down.
|
|
||||||
///
|
|
||||||
/// @note this method may throw
|
|
||||||
///
|
|
||||||
/// @param config_file Config file name (may be empty if unused).
|
|
||||||
virtual void init(const std::string& config_file);
|
|
||||||
|
|
||||||
/// @brief Performs final deconfiguration.
|
/// @brief Performs final deconfiguration.
|
||||||
///
|
///
|
||||||
/// Performs configuration backend specific final clean-up. This is called
|
/// Performs configuration backend specific final clean-up. This is called
|
||||||
@@ -103,11 +82,6 @@ public:
|
|||||||
/// @brief Initiates shutdown procedure for the whole DHCPv6 server.
|
/// @brief Initiates shutdown procedure for the whole DHCPv6 server.
|
||||||
virtual void shutdown();
|
virtual void shutdown();
|
||||||
|
|
||||||
/// @brief Returns config file name.
|
|
||||||
static std::string getConfigFile() {
|
|
||||||
return (config_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Initializes logger
|
/// @brief Initializes logger
|
||||||
///
|
///
|
||||||
/// This method initializes logging system. It also sets the default
|
/// This method initializes logging system. It also sets the default
|
||||||
@@ -161,6 +135,60 @@ public:
|
|||||||
/// @return text string
|
/// @return text string
|
||||||
static std::string getVersion(bool extended);
|
static std::string getVersion(bool extended);
|
||||||
|
|
||||||
|
/// @brief Returns config file name.
|
||||||
|
/// @return text string
|
||||||
|
std::string getConfigFile() const;
|
||||||
|
|
||||||
|
/// @brief Sets the configuration file name
|
||||||
|
///
|
||||||
|
/// @param config_file pathname of the configuration file
|
||||||
|
void setConfigFile(const std::string& config_file);
|
||||||
|
|
||||||
|
/// @brief returns the process name
|
||||||
|
/// This value is used as when forming the default PID file name
|
||||||
|
/// @return text string
|
||||||
|
std::string getProcName() const;
|
||||||
|
|
||||||
|
/// @brief Sets the process name
|
||||||
|
/// @param proc_name name the process by which the process is recognized
|
||||||
|
void setProcName(const std::string& proc_name);
|
||||||
|
|
||||||
|
/// @brief Returns the directory used when forming default PID file name
|
||||||
|
/// @return text string
|
||||||
|
std::string getPIDFileDir() const;
|
||||||
|
|
||||||
|
/// @brief Sets the PID file directory
|
||||||
|
/// @param pid_file_dir path into which the PID file should be written
|
||||||
|
/// Note the value should not include a trailing slash, '/'
|
||||||
|
void setPIDFileDir(const std::string& pid_file_dir);
|
||||||
|
|
||||||
|
/// @brief Returns the current PID file name
|
||||||
|
/// @return text string
|
||||||
|
std::string getPIDFileName() const;
|
||||||
|
|
||||||
|
/// @brief Sets PID file name
|
||||||
|
///
|
||||||
|
/// If this method is called prior to calling createPIDFile,
|
||||||
|
/// the value passed in will be treated as the full file name
|
||||||
|
/// for the PID file. This provides a means to override the
|
||||||
|
/// default file name with an explicit value.
|
||||||
|
///
|
||||||
|
/// @param pid_file_name file name to be used as the PID file
|
||||||
|
void setPIDFileName(const std::string& pid_file_name);
|
||||||
|
|
||||||
|
/// @brief Creates the PID file
|
||||||
|
///
|
||||||
|
/// If the PID file name has not been previously set, the method
|
||||||
|
/// uses manufacturePIDFileName() to set it. If the PID file
|
||||||
|
/// name refers to an existing file whose contents are a PID whose
|
||||||
|
/// process is still alive, the method will throw a DaemonPIDExists
|
||||||
|
/// exception. Otherwise, the file created (or truncated) and
|
||||||
|
/// the given pid (if not zero) is written to the file.
|
||||||
|
///
|
||||||
|
/// @param pid PID to write to the file if not zero, otherwise the
|
||||||
|
/// PID of the current process is used.
|
||||||
|
void createPIDFile(int pid = 0);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/// @brief Invokes handler for the next received signal.
|
/// @brief Invokes handler for the next received signal.
|
||||||
@@ -189,11 +217,25 @@ protected:
|
|||||||
/// it not initialized, the signals will not be handled.
|
/// it not initialized, the signals will not be handled.
|
||||||
isc::util::SignalHandler signal_handler_;
|
isc::util::SignalHandler signal_handler_;
|
||||||
|
|
||||||
|
/// @brief Manufacture the pid file name
|
||||||
|
std::string makePIDFileName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @brief Config file name or empty if config file not used.
|
/// @brief Config file name or empty if config file not used.
|
||||||
static std::string config_file_;
|
std::string config_file_;
|
||||||
|
|
||||||
|
/// @brief Name of this process, used when creating its pid file
|
||||||
|
std::string proc_name_;
|
||||||
|
|
||||||
|
/// @brief Pointer to the directory where PID file(s) are written
|
||||||
|
/// It defaults to --localstatedir
|
||||||
|
std::string pid_file_dir_;
|
||||||
|
|
||||||
|
/// @brief Pointer to the PID file for this process
|
||||||
|
isc::util::PIDFilePtr pid_file_;
|
||||||
|
|
||||||
|
/// @brief Flag indicating if this instance created the file
|
||||||
|
bool am_file_author_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // end of isc::dhcp namespace
|
}; // end of isc::dhcp namespace
|
||||||
|
@@ -34,6 +34,8 @@ namespace dhcp {
|
|||||||
class DaemonImpl : public Daemon {
|
class DaemonImpl : public Daemon {
|
||||||
public:
|
public:
|
||||||
static std::string getVersion(bool extended);
|
static std::string getVersion(bool extended);
|
||||||
|
|
||||||
|
using Daemon::makePIDFileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string DaemonImpl::getVersion(bool extended) {
|
std::string DaemonImpl::getVersion(bool extended) {
|
||||||
@@ -75,6 +77,172 @@ TEST_F(DaemonTest, constructor) {
|
|||||||
// Check that the verbose mode is not set by default.
|
// Check that the verbose mode is not set by default.
|
||||||
Daemon instance2;
|
Daemon instance2;
|
||||||
EXPECT_FALSE(instance2.getVerbose());
|
EXPECT_FALSE(instance2.getVerbose());
|
||||||
|
|
||||||
|
EXPECT_TRUE(instance2.getConfigFile().empty());
|
||||||
|
EXPECT_TRUE(instance2.getProcName().empty());
|
||||||
|
EXPECT_EQ(CfgMgr::instance().getDataDir(),instance2.getPIDFileDir());
|
||||||
|
EXPECT_TRUE(instance2.getPIDFileName().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify config file accessors
|
||||||
|
TEST_F(DaemonTest, getSetConfigFile) {
|
||||||
|
Daemon instance;
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(instance.setConfigFile("test.txt"));
|
||||||
|
EXPECT_EQ("test.txt", instance.getConfigFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify process name accessors
|
||||||
|
TEST_F(DaemonTest, getSetProcName) {
|
||||||
|
Daemon instance;
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(instance.setProcName("myproc"));
|
||||||
|
EXPECT_EQ("myproc", instance.getProcName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify PID file directory name accessors
|
||||||
|
TEST_F(DaemonTest, getSetPIDFileDir) {
|
||||||
|
Daemon instance;
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(instance.setPIDFileDir("/tmp"));
|
||||||
|
EXPECT_EQ("/tmp", instance.getPIDFileDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify PID file name accessors.
|
||||||
|
TEST_F(DaemonTest, setPIDFileName) {
|
||||||
|
Daemon instance;
|
||||||
|
|
||||||
|
// Verify that PID file name may not be set to empty
|
||||||
|
EXPECT_THROW(instance.setPIDFileName(""), BadValue);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(instance.setPIDFileName("myproc"));
|
||||||
|
EXPECT_EQ("myproc", instance.getPIDFileName());
|
||||||
|
|
||||||
|
// Verify that setPIDFileName cannot be called twice on the same instance.
|
||||||
|
EXPECT_THROW(instance.setPIDFileName("again"), InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the getVersion() redefinition
|
||||||
|
TEST_F(DaemonTest, getVersion) {
|
||||||
|
EXPECT_THROW(Daemon::getVersion(false), NotImplemented);
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(DaemonImpl::getVersion(false));
|
||||||
|
|
||||||
|
EXPECT_EQ(DaemonImpl::getVersion(false), "BASIC");
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(DaemonImpl::getVersion(true));
|
||||||
|
|
||||||
|
EXPECT_EQ(DaemonImpl::getVersion(true), "EXTENDED");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify makePIDFileName method
|
||||||
|
TEST_F(DaemonTest, makePIDFileName) {
|
||||||
|
DaemonImpl instance;
|
||||||
|
|
||||||
|
// Verify that config file cannot be blank
|
||||||
|
instance.setProcName("notblank");
|
||||||
|
EXPECT_THROW(instance.makePIDFileName(), InvalidOperation);
|
||||||
|
|
||||||
|
// Verify that proc name cannot be blank
|
||||||
|
instance.setProcName("");
|
||||||
|
instance.setConfigFile("notblank");
|
||||||
|
EXPECT_THROW(instance.makePIDFileName(), InvalidOperation);
|
||||||
|
|
||||||
|
// Verify that config file must contain a file name
|
||||||
|
instance.setProcName("myproc");
|
||||||
|
instance.setConfigFile(".txt");
|
||||||
|
EXPECT_THROW(instance.makePIDFileName(), BadValue);
|
||||||
|
instance.setConfigFile("/tmp/");
|
||||||
|
EXPECT_THROW(instance.makePIDFileName(), BadValue);
|
||||||
|
|
||||||
|
// Given a valid config file name and proc name we should good to go
|
||||||
|
instance.setConfigFile("/tmp/test.conf");
|
||||||
|
std::string name;
|
||||||
|
EXPECT_NO_THROW(name = instance.makePIDFileName());
|
||||||
|
|
||||||
|
// Make sure the name is as we expect
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << CfgMgr::instance().getDataDir() << "/test.myproc.pid";
|
||||||
|
EXPECT_EQ(stream.str(), name);
|
||||||
|
|
||||||
|
// Verify that the default directory can be overridden
|
||||||
|
instance.setPIDFileDir("/tmp");
|
||||||
|
EXPECT_NO_THROW(name = instance.makePIDFileName());
|
||||||
|
EXPECT_EQ("/tmp/test.myproc.pid", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies the creation a PID file and that a pre-existing PID file
|
||||||
|
// which points to a live PID causes a throw.
|
||||||
|
TEST_F(DaemonTest, createPIDFile) {
|
||||||
|
DaemonImpl instance;
|
||||||
|
|
||||||
|
instance.setConfigFile("test.conf");
|
||||||
|
instance.setProcName("daemon_test");
|
||||||
|
instance.setPIDFileDir(TEST_DATA_BUILDDIR);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(instance.createPIDFile());
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << TEST_DATA_BUILDDIR << "/test.daemon_test.pid";
|
||||||
|
EXPECT_EQ(stream.str(), instance.getPIDFileName());
|
||||||
|
|
||||||
|
// If we try again, we should see our own PID file and fail
|
||||||
|
EXPECT_THROW(instance.createPIDFile(), DaemonPIDExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies that a pre-existing PID file which points to a dead PID
|
||||||
|
// is overwritten.
|
||||||
|
TEST_F(DaemonTest, createPIDFileOverwrite) {
|
||||||
|
DaemonImpl instance;
|
||||||
|
|
||||||
|
// We're going to use fork to generate a PID we KNOW is dead.
|
||||||
|
int pid = fork();
|
||||||
|
ASSERT_GE(pid, 0);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// This is the child, die right away. Tragic, no?
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back in the parent test, we need to wait for the child to die
|
||||||
|
int stat;
|
||||||
|
int ret = waitpid(pid, &stat, 0);
|
||||||
|
ASSERT_EQ(ret, pid);
|
||||||
|
|
||||||
|
// Ok, so we should now have a PID that we know to be dead.
|
||||||
|
// Let's use it to create a PID file.
|
||||||
|
instance.setConfigFile("test.conf");
|
||||||
|
instance.setProcName("daemon_test");
|
||||||
|
instance.setPIDFileDir(TEST_DATA_BUILDDIR);
|
||||||
|
EXPECT_NO_THROW(instance.createPIDFile(pid));
|
||||||
|
|
||||||
|
// If we try to create the PID file again, this should work.
|
||||||
|
EXPECT_NO_THROW(instance.createPIDFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies that Daemon destruction deletes the PID file
|
||||||
|
TEST_F(DaemonTest, PIDFileCleanup) {
|
||||||
|
boost::shared_ptr<DaemonImpl> instance;
|
||||||
|
instance.reset(new DaemonImpl);
|
||||||
|
|
||||||
|
instance->setConfigFile("test.conf");
|
||||||
|
instance->setProcName("daemon_test");
|
||||||
|
instance->setPIDFileDir(TEST_DATA_BUILDDIR);
|
||||||
|
EXPECT_NO_THROW(instance->createPIDFile());
|
||||||
|
|
||||||
|
// If we try again, we should see our own PID file
|
||||||
|
EXPECT_THROW(instance->createPIDFile(), DaemonPIDExists);
|
||||||
|
|
||||||
|
// Save the pid file name
|
||||||
|
std::string pid_file_name = instance->getPIDFileName();
|
||||||
|
|
||||||
|
// Now delete the Daemon instance. This should remove the
|
||||||
|
// PID file.
|
||||||
|
instance.reset();
|
||||||
|
|
||||||
|
struct stat stat_buf;
|
||||||
|
ASSERT_EQ(-1, stat(pid_file_name.c_str(), &stat_buf));
|
||||||
|
EXPECT_EQ(errno, ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that configureLogger method is behaving properly.
|
// Checks that configureLogger method is behaving properly.
|
||||||
@@ -117,18 +285,6 @@ TEST_F(DaemonTest, parsingConsoleOutput) {
|
|||||||
EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_);
|
EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the getVersion() redefinition
|
|
||||||
TEST_F(DaemonTest, getVersion) {
|
|
||||||
EXPECT_THROW(Daemon::getVersion(false), NotImplemented);
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(DaemonImpl::getVersion(false));
|
|
||||||
|
|
||||||
EXPECT_EQ(DaemonImpl::getVersion(false), "BASIC");
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(DaemonImpl::getVersion(true));
|
|
||||||
|
|
||||||
EXPECT_EQ(DaemonImpl::getVersion(true), "EXTENDED");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// More tests will appear here as we develop Daemon class.
|
// More tests will appear here as we develop Daemon class.
|
||||||
|
@@ -426,6 +426,60 @@ must be a number"
|
|||||||
kill -${sig} ${_GET_PIDS}
|
kill -${sig} ${_GET_PIDS}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Verifies that a server is up running by its PID file
|
||||||
|
# The PID file is constructed from the given config file name and
|
||||||
|
# binary name. If it exists and the PID it contains refers to a
|
||||||
|
# live process it sets _SERVER_PID_FILE and _SERVER_PID to the
|
||||||
|
# corresponding values. Otherwise, it emits an error and exits.
|
||||||
|
verify_server_pid() {
|
||||||
|
local bin_name="${1}" # binary name of the server
|
||||||
|
local cfg_file="${2}" # config file name
|
||||||
|
|
||||||
|
# We will construct the PID file name based on the server config
|
||||||
|
# and binary name
|
||||||
|
if [ -z ${bin_name} ]; then
|
||||||
|
test_lib_error "verify_server_pid requires binary name"
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z ${cfg_file} ]; then
|
||||||
|
test_lib_error "verify_server_pid requires config file name"
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only the file name portion of the config file is used, try and
|
||||||
|
# extract it. NOTE if this "algorithm" changes this code will need
|
||||||
|
# to be updated.
|
||||||
|
fname=`basename ${cfg_file}`
|
||||||
|
fname=`echo $fname | cut -f1 -d'.'`
|
||||||
|
|
||||||
|
if [ -z ${fname} ]; then
|
||||||
|
test_lib_error "verify_server_pid could not extract config name"
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now we can build the name:
|
||||||
|
pid_file="$KEA_PIDFILE_DIR/$fname.$bin_name.pid"
|
||||||
|
|
||||||
|
if [ ! -e ${pid_file} ]; then
|
||||||
|
printf "ERROR: PID file:[%s] does not exist\n" ${pid_file}
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# File exists, does its PID point to a live process?
|
||||||
|
pid=`cat ${pid_file}`
|
||||||
|
kill -0 ${pid}
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
printf "ERROR: PID file:[%s] exists but PID:[%d] does not\n" \
|
||||||
|
${pid_file} ${pid}
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make the values accessible to the caller
|
||||||
|
_SERVER_PID="${pid}"
|
||||||
|
_SERVER_PID_FILE="${pid_file}"
|
||||||
|
}
|
||||||
|
|
||||||
# This test verifies that the binary is reporting its version properly.
|
# This test verifies that the binary is reporting its version properly.
|
||||||
version_test() {
|
version_test() {
|
||||||
test_name=${1} # Test name
|
test_name=${1} # Test name
|
||||||
@@ -536,3 +590,49 @@ logger_vars_test() {
|
|||||||
|
|
||||||
test_finish 0
|
test_finish 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This test verifies server PID file management
|
||||||
|
# 1. It verifies that upon startup, the server creates a PID file
|
||||||
|
# 2. It verifies the an attempt to start a second instance fails
|
||||||
|
# due to pre-existing PID File/PID detection
|
||||||
|
server_pid_file_test() {
|
||||||
|
local server_cfg="${1}"
|
||||||
|
local log_id="${2}"
|
||||||
|
|
||||||
|
# Log the start of the test and print test name.
|
||||||
|
test_start "${bin}.server_pid_file_test"
|
||||||
|
# Remove dangling DHCP4 instances and remove log files.
|
||||||
|
cleanup
|
||||||
|
# Create new configuration file.
|
||||||
|
create_config "${CONFIG}"
|
||||||
|
# Instruct server to log to the specific file.
|
||||||
|
set_logger
|
||||||
|
# Start server
|
||||||
|
start_kea ${bin_path}/${bin}
|
||||||
|
# Wait up to 20s for server to start.
|
||||||
|
wait_for_kea 20
|
||||||
|
if [ ${_WAIT_FOR_KEA} -eq 0 ]; then
|
||||||
|
printf "ERROR: timeout waiting for %s to start.\n" ${bin}
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify server is still running
|
||||||
|
verify_server_pid ${bin} ${CFG_FILE}
|
||||||
|
|
||||||
|
printf "PID file is [%s], PID is [%d]" ${_SERVER_PID_FILE} ${_SERVER_PID}
|
||||||
|
|
||||||
|
# Now try to start a second one
|
||||||
|
start_kea ${bin_path}/${bin}
|
||||||
|
|
||||||
|
wait_for_message 10 "${log_id}" 1
|
||||||
|
if [ ${_WAIT_FOR_MESSAGE} -eq 0 ]; then
|
||||||
|
printf "ERROR: Second %s instance started? PID conflict not reported.\n" ${bin}
|
||||||
|
clean_exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify server is still running
|
||||||
|
verify_server_pid ${bin} ${CFG_FILE}
|
||||||
|
|
||||||
|
# All ok. Shut down the server and exit.
|
||||||
|
test_finish 0
|
||||||
|
}
|
||||||
|
@@ -28,7 +28,7 @@ PIDFile::PIDFile(const std::string& filename)
|
|||||||
PIDFile::~PIDFile() {
|
PIDFile::~PIDFile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
int
|
||||||
PIDFile::check() const {
|
PIDFile::check() const {
|
||||||
std::ifstream fs(filename_.c_str());
|
std::ifstream fs(filename_.c_str());
|
||||||
int pid;
|
int pid;
|
||||||
@@ -51,13 +51,13 @@ PIDFile::check() const {
|
|||||||
<< filename_ << "'");
|
<< filename_ << "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the process is still running return true
|
// If the process is still running return its pid.
|
||||||
if (kill(pid, 0) == 0) {
|
if (kill(pid, 0) == 0) {
|
||||||
return (true);
|
return (pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No process
|
// No process
|
||||||
return (false);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#define PID_FILE_H
|
#define PID_FILE_H
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -62,11 +63,11 @@ public:
|
|||||||
/// If the file exists but can't be read or it doesn't have
|
/// If the file exists but can't be read or it doesn't have
|
||||||
/// the proper format treat it as the process existing.
|
/// the proper format treat it as the process existing.
|
||||||
///
|
///
|
||||||
/// @return true if the PID is in use, false otherwise
|
/// @return returns the PID if it is in, 0 otherwise.
|
||||||
///
|
///
|
||||||
/// @throw throws PIDCantReadPID if it was able to open the file
|
/// @throw throws PIDCantReadPID if it was able to open the file
|
||||||
/// but was unable to read the PID from it.
|
/// but was unable to read the PID from it.
|
||||||
bool check() const;
|
int check() const;
|
||||||
|
|
||||||
/// @brief Write the PID to the file.
|
/// @brief Write the PID to the file.
|
||||||
///
|
///
|
||||||
@@ -95,6 +96,9 @@ private:
|
|||||||
std::string filename_;
|
std::string filename_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Defines a shared pointer to a PIDFile
|
||||||
|
typedef boost::shared_ptr<PIDFile> PIDFilePtr;
|
||||||
|
|
||||||
} // namespace isc::util
|
} // namespace isc::util
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
|
@@ -127,7 +127,7 @@ TEST_F(PIDFileTest, pidInUse) {
|
|||||||
pid_file.write();
|
pid_file.write();
|
||||||
|
|
||||||
// Check if we think the process is running
|
// Check if we think the process is running
|
||||||
EXPECT_TRUE(pid_file.check());
|
EXPECT_EQ(getpid(), pid_file.check());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test checking a PID. Write a PID that isn't in use
|
/// @brief Test checking a PID. Write a PID that isn't in use
|
||||||
@@ -148,7 +148,7 @@ TEST_F(PIDFileTest, pidNotInUse) {
|
|||||||
pid_file.write(pid);
|
pid_file.write(pid);
|
||||||
|
|
||||||
// Check to see if we think the process is running
|
// Check to see if we think the process is running
|
||||||
if (!pid_file.check()) {
|
if (pid_file.check() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ TEST_F(PIDFileTest, pidNotInUse) {
|
|||||||
pid_file.write(pid);
|
pid_file.write(pid);
|
||||||
|
|
||||||
// Check to see if we think the process is running
|
// Check to see if we think the process is running
|
||||||
EXPECT_FALSE(pid_file.check());
|
EXPECT_EQ(0, pid_file.check());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Test checking a PID. Write garbage to the PID file
|
/// @brief Test checking a PID. Write garbage to the PID file
|
||||||
|
Reference in New Issue
Block a user