main.cc 8.76 KB
Newer Older
1
// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 7

#include <config.h>
8

9
#include <dhcp4/ctrl_dhcp4_srv.h>
10
#include <dhcp4/dhcp4_log.h>
11
#include <dhcp4/parser_context.h>
12 13
#include <dhcp4/json_config_parser.h>
#include <cc/command_interpreter.h>
14
#include <dhcpsrv/cfgmgr.h>
15
#include <log/logger_support.h>
16
#include <log/logger_manager.h>
17
#include <cfgrpt/config_report.h>
18

19 20 21 22
#include <boost/lexical_cast.hpp>

#include <iostream>

23
using namespace isc::data;
24
using namespace isc::dhcp;
25
using namespace std;
26

27
/// This file contains entry point (main() function) for standard DHCPv4 server
28
/// component of Kea software suite. It parses command-line arguments and
29 30 31
/// instantiates ControlledDhcpv4Srv class that is responsible for establishing
/// connection with msgq (receiving commands and configuration) and also
/// creating Dhcpv4 server object as well.
32

33
namespace {
34

35
const char* const DHCP4_NAME = "kea-dhcp4";
36

37 38 39
/// @brief Prints Kea Usage and exits
///
/// Note: This function never returns. It terminates the process.
40 41
void
usage() {
42 43 44
    cerr << "Kea DHCPv4 server, version " << VERSION << endl;
    cerr << endl;
    cerr << "Usage: " << DHCP4_NAME
45
         << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number]" << endl;
46 47
    cerr << "  -v: print version number and exit" << endl;
    cerr << "  -V: print extended version and exit" << endl;
48
    cerr << "  -W: display the configuration report and exit" << endl;
49 50
    cerr << "  -d: debug mode with extra verbosity (former -v)" << endl;
    cerr << "  -c file: specify configuration file" << endl;
51
    cerr << "  -t file: check the configuration file syntax and exit" << endl;
52 53
    cerr << "  -p number: specify non-standard port number 1-65535 "
         << "(useful for testing only)" << endl;
54
    exit(EXIT_FAILURE);
55 56 57 58 59 60
}
} // end of anonymous namespace

int
main(int argc, char* argv[]) {
    int ch;
61 62
    int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                         // useful for testing only.
63
    bool verbose_mode = false; // Should server be verbose?
64
    bool check_mode = false;   // Check syntax
65

66 67 68
    // The standard config file
    std::string config_file("");

69
    while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) {
70
        switch (ch) {
71
        case 'd':
72 73
            verbose_mode = true;
            break;
74

75
        case 'v':
76
            cout << Dhcpv4Srv::getVersion(false) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
77
            return (EXIT_SUCCESS);
78 79

        case 'V':
80
            cout << Dhcpv4Srv::getVersion(true) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
81
            return (EXIT_SUCCESS);
82

83 84 85 86
        case 'W':
            cout << isc::detail::getConfigReport() << endl;
            return (EXIT_SUCCESS);

87 88 89 90
        case 't':
            check_mode = true;
            // falls through

91 92 93 94
        case 'c': // config file
            config_file = optarg;
            break;

95
        case 'p':
96 97 98 99 100 101 102 103
            try {
                port_number = boost::lexical_cast<int>(optarg);
            } catch (const boost::bad_lexical_cast &) {
                cerr << "Failed to parse port number: [" << optarg
                     << "], 1-65535 allowed." << endl;
                usage();
            }
            if (port_number <= 0 || port_number > 65535) {
104 105 106 107 108
                cerr << "Failed to parse port number: [" << optarg
                     << "], 1-65535 allowed." << endl;
                usage();
            }
            break;
109

110 111 112 113 114
        default:
            usage();
        }
    }

115
    // Check for extraneous parameters.
116
    if (argc > optind) {
117 118 119
        usage();
    }

120

121 122 123 124 125 126
    // Configuration file is required.
    if (config_file.empty()) {
        cerr << "Configuration file not specified." << endl;
        usage();
    }

127 128 129
    // This is the DHCPv4 server
    CfgMgr::instance().setFamily(AF_INET);

130 131
    if (check_mode) {
        try {
132 133 134 135 136 137 138 139

            // We need to initialize logging, in case any error messages are to be printed.
            // This is just a test, so we don't care about lockfile.
            setenv("KEA_LOCKFILE_DIR", "none", 0);
            CfgMgr::instance().setDefaultLoggerName(DHCP4_ROOT_LOGGER_NAME);
            Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);

            // Check the syntax first.
140 141 142 143 144 145 146 147 148 149
            Parser4Context parser;
            ConstElementPtr json;
            json = parser.parseFile(config_file, Parser4Context::PARSER_DHCP4);
            if (!json) {
                cerr << "No configuration found" << endl;
                return (EXIT_FAILURE);
            }
            if (verbose_mode) {
                cerr << "Syntax check OK" << endl;
            }
150 151 152 153 154 155 156 157 158

            // Check the logic next.
            ConstElementPtr dhcp4 = json->get("Dhcp4");
            if (!dhcp4) {
                cerr << "Missing mandatory Dhcp4 element" << endl;
                return (EXIT_FAILURE);
            }
            ControlledDhcpv4Srv server(0);
            ConstElementPtr answer;
159 160 161

            // Now we pass the Dhcp4 configuration to the server, but
            // tell it to check the configuration only (check_only = true)
162 163 164 165 166 167 168 169 170 171
            answer = configureDhcp4Server(server, dhcp4, true);

            int status_code = 0;
            answer = isc::config::parseAnswer(status_code, answer);
            if (status_code == 0) {
                return (EXIT_SUCCESS);
            } else {
                cerr << "Error encountered: " << answer->stringValue() << endl;
                return (EXIT_FAILURE);
            }
172
        } catch (const std::exception& ex) {
173
            cerr << "Syntax check failed with: " << ex.what() << endl;
174 175 176 177
        }
        return (EXIT_FAILURE);
    }

178
    int ret = EXIT_SUCCESS;
179
    try {
180 181 182
        // 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
        // in the Kea configuration file.
183
        CfgMgr::instance().setDefaultLoggerName(DHCP4_ROOT_LOGGER_NAME);
184

185
        // Initialize logging.  If verbose, we'll use maximum verbosity.
186
        Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);
187
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
188 189
            .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no");

190
        LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION);
191

Tomek Mrugalski's avatar
Tomek Mrugalski committed
192
        // Create the server instance.
193
        ControlledDhcpv4Srv server(port_number);
194

Tomek Mrugalski's avatar
Tomek Mrugalski committed
195 196 197
        // Remember verbose-mode
        server.setVerbose(verbose_mode);

198 199 200
        // Create our PID file.
        server.setProcName(DHCP4_NAME);
        server.setConfigFile(config_file);
201 202
        server.createPIDFile();

Tomek Mrugalski's avatar
Tomek Mrugalski committed
203 204 205 206
        try {
            // Initialize the server.
            server.init(config_file);
        } catch (const std::exception& ex) {
207

Tomek Mrugalski's avatar
Tomek Mrugalski committed
208 209 210 211 212 213
            try {
                // Let's log out what went wrong.
                isc::log::LoggerManager log_manager;
                log_manager.process();
                LOG_ERROR(dhcp4_logger, DHCP4_INIT_FAIL).arg(ex.what());
            } catch (...) {
Francis Dupont's avatar
Francis Dupont committed
214 215 216
                // The exception thrown during the initialization could
                // originate from logger subsystem. Therefore LOG_ERROR()
                // may fail as well.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
217 218
                cerr << "Failed to initialize server: " << ex.what() << endl;
            }
219

Tomek Mrugalski's avatar
Tomek Mrugalski committed
220
            return (EXIT_FAILURE);
221
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
222

223 224 225
        // Tell the admin we are ready to process packets
        LOG_INFO(dhcp4_logger, DHCP4_STARTED).arg(VERSION);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
226
        // And run the main loop of the server.
227
        server.run();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
228

229 230
        LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);

231 232 233 234
    } 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;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
235

236 237 238
        // 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)
239 240 241 242 243 244
        try {
            LOG_FATAL(dhcp4_logger, DHCP4_ALREADY_RUNNING)
                .arg(DHCP4_NAME).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
245 246
        ret = EXIT_FAILURE;
    } catch (const std::exception& ex) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
247 248 249 250 251 252 253
        // First, we print the error on stderr (that should always work)
        cerr << DHCP4_NAME << ": Fatal error during start up: " << 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)
254 255 256 257 258
        try {
            LOG_FATAL(dhcp4_logger, DHCP4_SERVER_FAILED).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
259
        ret = EXIT_FAILURE;
260 261 262 263
    }

    return (ret);
}