Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
4a2f0998
Commit
4a2f0998
authored
Jun 02, 2015
by
Tomek Mrugalski
🛰
Browse files
[3880] Callback for handling UNIX connections implemented.
parent
16ccf1ae
Changes
9
Hide whitespace changes
Inline
Side-by-side
src/lib/Makefile.am
View file @
4a2f0998
# The following build order must be maintained.
SUBDIRS
=
exceptions util log hooks cryptolink dns cc stats config
\
asiolink asiodns testutils
dhcp
dhcp_ddns dhcpsrv
SUBDIRS
=
exceptions util log hooks cryptolink dns cc stats
dhcp
config
\
asiolink asiodns testutils dhcp_ddns dhcpsrv
src/lib/config/Makefile.am
View file @
4a2f0998
...
...
@@ -25,6 +25,7 @@ libkea_cfgclient_la_SOURCES += config_log.h config_log.cc
libkea_cfgclient_la_LIBADD
=
$(top_builddir)
/src/lib/cc/libkea-cc.la
libkea_cfgclient_la_LIBADD
+=
$(top_builddir)
/src/lib/exceptions/libkea-exceptions.la
libkea_cfgclient_la_LIBADD
+=
$(top_builddir)
/src/lib/log/libkea-log.la
libkea_cfgclient_la_LIBADD
+=
$(top_builddir)
/src/lib/dhcp/libkea-dhcp++.la
libkea_cfgclient_la_LDFLAGS
=
-no-undefined
-version-info
1:0:1
...
...
src/lib/config/command_mgr.cc
View file @
4a2f0998
...
...
@@ -15,8 +15,12 @@
#include
<config/command_mgr.h>
#include
<config/command_socket_factory.h>
#include
<cc/data.h>
#include
<dhcp/iface_mgr.h>
#include
<config/config_log.h>
#include
<boost/bind.hpp>
using
namespace
isc
::
data
;
namespace
isc
{
namespace
config
{
...
...
@@ -33,12 +37,18 @@ int CommandMgr::openCtrlSocket(const isc::data::ConstElementPtr& socket_info) {
socket_
=
CommandSocketFactory
::
create
(
socket_info
);
socket_info_
=
socket_info
;
/// @todo: install socket in IfaceMgr
///CommandSocketFactory::install(socket_, socket_info);
// Install this socket in Interface Manager.
isc
::
dhcp
::
IfaceMgr
::
instance
().
addExternalSocket
(
socket_
,
boost
::
bind
(
&
isc
::
config
::
CommandMgr
::
connectionAcceptor
,
socket_
));
return
(
socket_
);
}
void
CommandMgr
::
closeCtrlSocket
()
{
if
(
socket_info_
)
{
isc
::
dhcp
::
IfaceMgr
::
instance
().
deleteExternalSocket
(
socket_
);
CommandSocketFactory
::
close
(
socket_
,
socket_info_
);
socket_
=
0
;
socket_info_
.
reset
();
...
...
@@ -64,6 +74,8 @@ void CommandMgr::registerCommand(const std::string& cmd, CommandHandler handler)
}
handlers_
.
insert
(
make_pair
(
cmd
,
handler
));
LOG_DEBUG
(
command_logger
,
DBG_COMMAND
,
COMMAND_REGISTERED
).
arg
(
cmd
);
}
void
CommandMgr
::
deregisterCommand
(
const
std
::
string
&
cmd
)
{
...
...
@@ -78,14 +90,116 @@ void CommandMgr::deregisterCommand(const std::string& cmd) {
<<
"' not found."
);
}
handlers_
.
erase
(
it
);
LOG_DEBUG
(
command_logger
,
DBG_COMMAND
,
COMMAND_DEREGISTERED
).
arg
(
cmd
);
}
void
CommandMgr
::
deregisterAll
()
{
// No need to log anything here. deregisterAll is not used in production
// code, just in tests.
handlers_
.
clear
();
registerCommand
(
"list-commands"
,
boost
::
bind
(
&
CommandMgr
::
listCommandsHandler
,
this
,
_1
,
_2
));
}
void
CommandMgr
::
connectionAcceptor
(
int
sockfd
)
{
/// @todo: Either make this generic or rename this method
/// to CommandSocketFactory::unixConnectionAcceptor
struct
sockaddr_un
client_addr
;
socklen_t
client_addr_len
;
client_addr_len
=
sizeof
(
client_addr
);
// Accept incoming connection. This will create a separate socket for
// handling this specific connection.
int
fd2
=
accept
(
sockfd
,
(
struct
sockaddr
*
)
&
client_addr
,
&
client_addr_len
);
// Not sure if this is really needed, but let's set it to non-blocking mode.
fcntl
(
fd2
,
F_SETFL
,
O_NONBLOCK
);
// Install commandReader callback. When there's any data incoming on this
// socket, commandReader will be called and process it. It may also
// eventually close this socket.
isc
::
dhcp
::
IfaceMgr
::
instance
().
addExternalSocket
(
fd2
,
boost
::
bind
(
&
isc
::
config
::
CommandMgr
::
commandReader
,
fd2
));
LOG_INFO
(
command_logger
,
COMMAND_SOCKET_CONNECTION_OPENED
).
arg
(
fd2
).
arg
(
sockfd
);
}
void
CommandMgr
::
commandReader
(
int
sockfd
)
{
// We should not expect commands bigger than 64K.
char
buf
[
65536
];
memset
(
buf
,
0
,
sizeof
(
buf
));
ConstElementPtr
cmd
,
rsp
;
// Read incoming data.
int
rval
=
read
(
sockfd
,
buf
,
sizeof
(
buf
));
if
(
rval
<
0
)
{
// Read failed
LOG_WARN
(
command_logger
,
COMMAND_SOCKET_READ_FAIL
).
arg
(
rval
).
arg
(
sockfd
);
return
;
}
else
if
(
rval
==
0
)
{
// read of 0 bytes means end-of-file. In other words the connection is
// being closed.
LOG_INFO
(
command_logger
,
COMMAND_SOCKET_CONNECTION_CLOSED
).
arg
(
sockfd
);
// Unregister this callback
isc
::
dhcp
::
IfaceMgr
::
instance
().
deleteExternalSocket
(
sockfd
);
// Close the socket.
close
(
sockfd
);
return
;
}
LOG_DEBUG
(
command_logger
,
DBG_COMMAND
,
COMMAND_SOCKET_READ
).
arg
(
rval
).
arg
(
sockfd
);
// Ok, we received something. Let's see if we can make any sense of it.
try
{
// Try to interpret it as JSON.
cmd
=
Element
::
fromJSON
(
std
::
string
(
buf
),
true
);
// If successful, then process it as a command.
rsp
=
CommandMgr
::
instance
().
processCommand
(
cmd
);
}
catch
(
const
Exception
&
ex
)
{
LOG_WARN
(
command_logger
,
COMMAND_PROCESS_ERROR
).
arg
(
ex
.
what
());
rsp
=
createAnswer
(
CONTROL_RESULT_ERROR
,
std
::
string
(
ex
.
what
()));
}
if
(
!
rsp
)
{
LOG_WARN
(
command_logger
,
COMMAND_RESPONSE_ERROR
);
return
;
}
// Let's convert JSON response to text. Note that at this stage
// the rsp pointer is always set.
std
::
string
txt
=
rsp
->
str
();
size_t
len
=
txt
.
length
();
if
(
len
>
65535
)
{
// Hmm, our response is too large. Let's send the first
// 64KB and hope for the best.
len
=
65535
;
}
// Send the data back over socket.
rval
=
write
(
sockfd
,
txt
.
c_str
(),
len
);
LOG_DEBUG
(
command_logger
,
DBG_COMMAND
,
COMMAND_SOCKET_WRITE
).
arg
(
len
).
arg
(
sockfd
);
if
(
rval
<
0
)
{
// Response transmission failed. Since the response failed, it doesn't
// make sense to send any status codes. Let's log it and be done with
// it.
LOG_WARN
(
command_logger
,
COMMAND_SOCKET_WRITE_FAIL
).
arg
(
len
).
arg
(
sockfd
);
}
}
isc
::
data
::
ConstElementPtr
CommandMgr
::
processCommand
(
const
isc
::
data
::
ConstElementPtr
&
cmd
)
{
if
(
!
cmd
)
{
...
...
@@ -94,9 +208,11 @@ CommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) {
}
try
{
isc
::
data
::
ConstElementPtr
arg
;
ConstElementPtr
arg
;
std
::
string
name
=
parseCommand
(
arg
,
cmd
);
LOG_INFO
(
command_logger
,
COMMAND_RECEIVED
).
arg
(
name
);
HandlerContainer
::
const_iterator
it
=
handlers_
.
find
(
name
);
if
(
it
==
handlers_
.
end
())
{
// Ok, there's no such command.
...
...
@@ -112,7 +228,6 @@ CommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) {
std
::
string
(
"Error during command processing:"
)
+
e
.
what
()));
}
}
isc
::
data
::
ConstElementPtr
...
...
src/lib/config/command_mgr.h
View file @
4a2f0998
...
...
@@ -121,6 +121,26 @@ public:
/// it may be called by external code explicitly. Hence this method is public.
isc
::
data
::
ConstElementPtr
processCommand
(
const
isc
::
data
::
ConstElementPtr
&
cmd
);
/// @brief Callback used to accept incoming connections.
///
/// This callback is used on a control socket. Once called, it will accept
/// incoming connection, create new socket for it and install @ref commandReader
/// for that new socket in @ref isc::dhcp::IfaceMgr.
///
/// @param sockfd socket descriptor of a socket capable of accepting
/// incoming connections
static
void
connectionAcceptor
(
int
sockfd
);
/// @brief Reads data from a socket, parses as JSON command and processes it
///
/// This method is used to handle traffic on connected socket. This callback
/// is installed by the @ref connectionAcceptor once the incoming connection
/// is accepted. If end-of-file is detected, this method will close the socket
/// and will uninstall itself from @ref isc::dhcp::IfaceMgr.
///
/// @param sockfd socket descriptor of a connected socket
static
void
commandReader
(
int
sockfd
);
/// @brief Auxiliary method that removes all installed commands.
///
/// The only unwipeable method is list-commands, which is internally
...
...
src/lib/config/command_socket_factory.cc
View file @
4a2f0998
...
...
@@ -13,9 +13,17 @@
// PERFORMANCE OF THIS SOFTWARE.
#include
<config/command_socket_factory.h>
#include
<config/config_log.h>
#include
<cstdio>
#include
<sys/socket.h>
#include
<sys/un.h>
#include
<string.h>
#include
<unistd.h>
#include
<fcntl.h>
using
namespace
isc
::
config
;
namespace
{
int
createUnixSocket
(
const
std
::
string
&
file_name
)
{
...
...
@@ -24,8 +32,15 @@ int createUnixSocket(const std::string& file_name) {
isc_throw
(
isc
::
config
::
SocketError
,
"Failed to create AF_UNIX socket."
);
}
/// @todo: Do we need any setsockopt here?
// Let's remove the old file. We don't care about any possible
// errors here. The file should not be there if the file was
// shut down properly.
remove
(
file_name
.
c_str
());
// Set this socket to be non-blocking one.
fcntl
(
fd
,
F_SETFL
,
O_NONBLOCK
);
// Now bind the socket to the specified path.
struct
sockaddr_un
addr
;
memset
(
&
addr
,
0
,
sizeof
(
addr
));
addr
.
sun_family
=
AF_UNIX
;
...
...
@@ -35,14 +50,30 @@ int createUnixSocket(const std::string& file_name) {
<<
" to "
<<
file_name
);
}
// One means that we allow at most 1 awaiting connections.
// Any additional attempts will get ECONNREFUSED error.
// That means that at any given time, there may be at most one controlling
// connection.
/// @todo: Make this a configurable.
int
status
=
listen
(
fd
,
1
);
if
(
status
<
0
)
{
isc_throw
(
isc
::
config
::
SocketError
,
"Failed to listen on socket fd="
<<
fd
<<
", filename="
<<
file_name
);
}
LOG_INFO
(
command_logger
,
COMMAND_SOCKET_UNIX_OPEN
).
arg
(
fd
).
arg
(
file_name
);
return
(
fd
);
}
void
closeUnixSocket
(
int
socket_fd
,
const
std
::
string
&
file_name
)
{
LOG_INFO
(
command_logger
,
COMMAND_SOCKET_UNIX_CLOSE
).
arg
(
socket_fd
).
arg
(
file_name
);
close
(
socket_fd
);
unlink
(
file_name
.
c_str
());
}
}
using
namespace
isc
::
data
;
namespace
isc
{
...
...
src/lib/config/config_log.cc
View file @
4a2f0998
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011
,2015
Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
...
...
@@ -21,6 +21,8 @@ namespace config {
isc
::
log
::
Logger
config_logger
(
"config"
);
isc
::
log
::
Logger
command_logger
(
"commands"
);
}
// namespace nsas
}
// namespace isc
src/lib/config/config_log.h
View file @
4a2f0998
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011
,2015
Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
...
...
@@ -21,20 +21,24 @@
namespace
isc
{
namespace
config
{
/// \brief Config Logging
///
/// Defines logger object for config log messages
/// \brief Config Logger
/// @brief Command processing Logger
///
/// Define the logger used to log messages. We could define it in multiple
/// modules, but defining in a single module and linking to it saves time and
/// space.
extern
isc
::
log
::
Logger
config_logger
;
/// @brief Command processing Logger
///
/// Define the logger used to log messages related to command processing.
extern
isc
::
log
::
Logger
command_logger
;
// Enumerate configuration elements as they are processed.
const
int
DBG_CONFIG_PROCESS
=
DBGLVL_TRACE_BASIC
;
// Enumerate configuration elements as they are processed.
const
int
DBG_COMMAND
=
DBGLVL_COMMAND
;
}
// namespace config
}
// namespace isc
...
...
src/lib/config/config_messages.mes
View file @
4a2f0998
# Copyright (C) 2011, 2014 Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 2011, 2014
-2015
Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
...
...
@@ -14,6 +14,66 @@
$NAMESPACE isc::config
% COMMAND_PROCESS_ERROR Error while processing command: %1
This warning message indicates that the server encountered an error while
processing received command. Additional information will be provided, if
available. Additional log messages may provide more details.
% COMMAND_RECEIVED Received command '%1'
This informational message indicates that a command was received over command
socket. The nature of this command and its possible results will be logged
with separate messages.
% COMMAND_RESPONSE_ERROR Server failed to generate response for command: %1
This error message indicates that the server failed to generate response for
specified command. This likely indicates a server logic error, as the server
is expected to generate valid responses for all commands, even malformed
ones.
% COMMAND_SOCKET_READ Received %1 bytes over command socket %2
This debug message indicates that specified number of bytes was received
over command socket identified by specified file descriptor.
% COMMAND_SOCKET_READ_FAIL Encountered error %1 while reading from command socket %2
This warning message indicates that an error was encountered while
reading from command socket.
% COMMAND_SOCKET_WRITE Sent response of %1 bytes over command socket %2
This debug message indicates that the specified number of bytes was sent
over command socket identifier by the specified file descriptor.
% COMMAND_SOCKET_WRITE_FAIL Error while writing %1 bytes to command socket %2
This warning message indicates that an error was encountered while
attempting to send a response to the command socket.
% COMMAND_SOCKET_UNIX_OPEN Command socket opened: UNIX, fd=%1, path=%2
This informational message indicates that the daemon opened a command
processing socket. This is a UNIX socket. It was opened with the file
descriptor and path specified.
% COMMAND_SOCKET_UNIX_CLOSE Command socket closed: UNIX, fd=%1, path=%2
This informational message indicates that the daemon closed a command
processing socket. This was a UNIX socket. It was opened with the file
descriptor and path specified.
% COMMAND_SOCKET_CONNECTION_OPENED Opened socket %1 for incoming command connection on socket %2
This is an informational message that a new incoming command connection was
detected and a dedicated socket was opened for that connection.
% COMMAND_SOCKET_CONNECTION_CLOSED Closed socket %1 for existing command connection
This is an informational message that the socket created for handling
client's connection is closed. This usually means that the client disconnected,
but may also mean a timeout.
% COMMAND_REGISTERED Command %1 registered
This debug message indicates that the daemon started supporting specified
command. If the command socket is open, this command can now be issued.
% COMMAND_DEREGISTERED Command %1 deregistered
This debug message indicates that the daemon stopped supporting specified
command. This command can no longer be issued. If the command socket is
open and this command is issued, the daemon will not be able to process it.
% CONFIG_CCSESSION_MSG error in CC session message: %1
There was a problem with an incoming message on the command and control
channel. The message does not appear to be a valid command, and is
...
...
src/lib/config/tests/Makefile.am
View file @
4a2f0998
...
...
@@ -30,6 +30,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
run_unittests_LDADD
+=
$(top_builddir)
/src/lib/exceptions/libkea-exceptions.la
run_unittests_LDADD
+=
$(top_builddir)
/src/lib/log/libkea-log.la
run_unittests_LDADD
+=
$(top_builddir)
/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD
+=
$(top_builddir)
/src/lib/dhcp/libkea-dhcp++.la
endif
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment