Coverage Report

Created: 2025-12-31 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/config/hooked_command_mgr.cc
Line
Count
Source
1
// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC")
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
#include <config.h>
8
9
#include <cc/command_interpreter.h>
10
#include <config/hooked_command_mgr.h>
11
#include <config/config_log.h>
12
#include <hooks/callout_handle.h>
13
#include <hooks/hooks_manager.h>
14
#include <hooks/server_hooks.h>
15
#include <boost/pointer_cast.hpp>
16
#include <vector>
17
18
using namespace isc::data;
19
using namespace isc::hooks;
20
21
namespace isc {
22
namespace config {
23
24
HookedCommandMgr::HookedCommandMgr()
25
20
    : BaseCommandMgr() {
26
20
}
27
28
bool
29
HookedCommandMgr::delegateCommandToHookLibrary(const std::string& cmd_name,
30
                                               const ConstElementPtr& params,
31
                                               const ConstElementPtr& original_cmd,
32
20.9k
                                               ElementPtr& answer) {
33
34
20.9k
    ConstElementPtr hook_response;
35
20.9k
    if (HooksManager::commandHandlersPresent(cmd_name)) {
36
37
0
        CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
38
39
        // Set status to normal.
40
0
        callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
41
42
        // Delete previously set arguments.
43
0
        callout_handle->deleteAllArguments();
44
45
0
        ConstElementPtr command = original_cmd ? original_cmd :
46
0
            createCommand(cmd_name, params);
47
48
        // And pass it to the hook library.
49
0
        callout_handle->setArgument("command", command);
50
0
        callout_handle->setArgument("response", hook_response);
51
52
0
        HooksManager::callCommandHandlers(cmd_name, *callout_handle);
53
54
        // The callouts should set the response.
55
0
        callout_handle->getArgument("response", hook_response);
56
57
0
        answer = boost::const_pointer_cast<Element>(hook_response);
58
59
0
        return (true);
60
0
    }
61
62
20.9k
    return (false);
63
20.9k
}
64
65
ConstElementPtr
66
HookedCommandMgr::handleCommand(const std::string& cmd_name,
67
                                const ConstElementPtr& params,
68
20.9k
                                const ConstElementPtr& original_cmd) {
69
70
    // The 'list-commands' is a special case. Hook libraries do not implement
71
    // this command. We determine what commands are supported by the hook
72
    // libraries by checking what hook points are present that have callouts
73
    // registered.
74
20.9k
    if ((cmd_name != "list-commands")) {
75
20.9k
        ElementPtr hook_response;
76
        // Check if there are any hooks libraries to process this command.
77
20.9k
        if (delegateCommandToHookLibrary(cmd_name, params, original_cmd,
78
20.9k
                                         hook_response)) {
79
            // Hooks libraries processed this command so simply return a
80
            // result.
81
0
            return (hook_response);
82
0
        }
83
84
20.9k
    }
85
86
    // If we're here it means that the callouts weren't called. We need
87
    // to handle the command using local Command Manager.
88
20.9k
    ConstElementPtr response = BaseCommandMgr::handleCommand(cmd_name,
89
20.9k
                                                             params,
90
20.9k
                                                             original_cmd);
91
92
    // If we're processing 'list-commands' command we may need to include
93
    // commands supported by hooks libraries in the response.
94
20.9k
    if (cmd_name == "list-commands") {
95
        // Hooks names can be used to decode what commands are supported.
96
1
        const std::vector<std::string>& hooks =
97
1
            ServerHooks::getServerHooksPtr()->getHookNames();
98
99
        // Only update the response if there are any hooks present.
100
1
        if (!hooks.empty()) {
101
1
            ElementPtr hooks_commands = Element::createList();
102
27
            for (auto const& h : hooks) {
103
                // Try to convert hook name to command name. If non-empty
104
                // string is returned it means that the hook point may have
105
                // command handlers associated with it. Otherwise, it means that
106
                // existing hook points are not for command handlers but for
107
                // regular callouts.
108
27
                std::string command_name = ServerHooks::hookToCommandName(h);
109
27
                if (!command_name.empty()) {
110
                    // Final check: are command handlers registered for this
111
                    // hook point? If there are no command handlers associated,
112
                    // it means that the hook library was already unloaded.
113
0
                    if (HooksManager::commandHandlersPresent(command_name)) {
114
0
                        hooks_commands->add(Element::create(command_name));
115
0
                    }
116
0
                }
117
27
            }
118
119
            // If there is at least one hook point with command handlers
120
            // registered
121
            // for it, combine the lists of commands.
122
1
            if (!hooks_commands->empty()) {
123
0
                response = combineCommandsLists(response, createAnswer(CONTROL_RESULT_SUCCESS, hooks_commands));
124
0
            }
125
1
        }
126
1
    }
127
128
20.9k
    return (response);
129
20.9k
}
130
131
132
} // end of namespace isc::config
133
} // end of namespace isc