main.cc 8.8 KB
Newer Older
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1
// Copyright (C) 2011-2018 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
#include <kea_version.h>
9

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

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

#include <iostream>

24
using namespace isc::data;
25
using namespace isc::dhcp;
26
using namespace isc::process;
27
using namespace std;
28

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

35
namespace {
36

37
const char* const DHCP4_NAME = "kea-dhcp4";
38

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

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

68 69 70
    // The standard config file
    std::string config_file("");

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

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

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

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

89 90 91 92
        case 't':
            check_mode = true;
            // falls through

93 94 95 96
        case 'c': // config file
            config_file = optarg;
            break;

97
        case 'p':
98 99 100 101 102 103 104 105
            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) {
106 107 108 109 110
                cerr << "Failed to parse port number: [" << optarg
                     << "], 1-65535 allowed." << endl;
                usage();
            }
            break;
111

112 113 114 115 116
        default:
            usage();
        }
    }

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

122

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

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

132 133
    if (check_mode) {
        try {
134 135 136 137

            // 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);
138
            Daemon::setDefaultLoggerName(DHCP4_ROOT_LOGGER_NAME);
139 140 141
            Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);

            // Check the syntax first.
142 143 144 145 146 147 148 149 150 151
            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;
            }
152 153 154 155 156 157 158 159 160

            // 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;
161 162 163

            // Now we pass the Dhcp4 configuration to the server, but
            // tell it to check the configuration only (check_only = true)
164 165 166 167 168 169 170 171 172 173
            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);
            }
174
        } catch (const std::exception& ex) {
175
            cerr << "Syntax check failed with: " << ex.what() << endl;
176 177 178 179
        }
        return (EXIT_FAILURE);
    }

180
    int ret = EXIT_SUCCESS;
181
    try {
182 183 184
        // 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.
185
        Daemon::setDefaultLoggerName(DHCP4_ROOT_LOGGER_NAME);
186

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

192
        LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION);
193

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

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

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
210 211 212 213 214 215
            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
216 217 218
                // 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
219 220
                cerr << "Failed to initialize server: " << ex.what() << endl;
            }
221

Tomek Mrugalski's avatar
Tomek Mrugalski committed
222
            return (EXIT_FAILURE);
223
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
224

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

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

231 232
        LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);

233
    } catch (const isc::process::DaemonPIDExists& ex) {
234 235 236
        // 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
237

238 239 240
        // 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)
241 242 243 244 245 246
        try {
            LOG_FATAL(dhcp4_logger, DHCP4_ALREADY_RUNNING)
                .arg(DHCP4_NAME).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
247 248
        ret = EXIT_FAILURE;
    } catch (const std::exception& ex) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
249 250 251 252 253 254 255
        // 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)
256 257 258 259 260
        try {
            LOG_FATAL(dhcp4_logger, DHCP4_SERVER_FAILED).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
261
        ret = EXIT_FAILURE;
262 263 264 265
    }

    return (ret);
}