main.cc 9.06 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 8

#include <config.h>

9 10
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
Francis Dupont's avatar
Francis Dupont committed
11
#include <dhcp6/parser_context.h>
12
#include <dhcp6/json_config_parser.h>
13
#include <dhcpsrv/cfgmgr.h>
14
#include <log/logger_support.h>
15
#include <log/logger_manager.h>
16
#include <exceptions/exceptions.h>
17
#include <cfgrpt/config_report.h>
18

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

#include <iostream>

Francis Dupont's avatar
Francis Dupont committed
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 DHCPv6 server
28
/// component of Kea software suite. It parses command-line arguments and
29 30 31 32 33 34 35
/// instantiates ControlledDhcpv6Srv class that is responsible for establishing
/// connection with msgq (receiving commands and configuration) and also
/// creating Dhcpv6 server object as well.
///
/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.

36
namespace {
37
const char* const DHCP6_NAME = "kea-dhcp6";
38

Tomek Mrugalski's avatar
Tomek Mrugalski committed
39
const char* const DHCP6_LOGGER_NAME = "kea-dhcp6";
40

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

int
main(int argc, char* argv[]) {
    int ch;
65 66
    int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
                                         // useful for testing only.
67
    bool verbose_mode = false; // Should server be verbose?
Francis Dupont's avatar
Francis Dupont committed
68
    bool check_mode = false;   // Check syntax
69

70 71 72
    // The standard config file
    std::string config_file("");

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

79
        case 'v':
80
            cout << Dhcpv6Srv::getVersion(false) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
81
            return (EXIT_SUCCESS);
82 83

        case 'V':
84
            cout << Dhcpv6Srv::getVersion(true) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
85
            return (EXIT_SUCCESS);
86

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

91 92 93 94
        case 't':
            check_mode = true;
            // falls through

95 96 97 98
        case 'c': // config file
            config_file = optarg;
            break;

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

114 115 116 117 118
        default:
            usage();
        }
    }

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

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

130 131 132
    // This is the DHCPv6 server
    CfgMgr::instance().setFamily(AF_INET6);

Francis Dupont's avatar
Francis Dupont committed
133 134
    if (check_mode) {
        try {
135 136 137 138 139 140 141
            // 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(DHCP6_ROOT_LOGGER_NAME);
            Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode);

            // Check the syntax first.
Francis Dupont's avatar
Francis Dupont committed
142 143 144 145 146 147 148 149 150 151
            Parser6Context parser;
            ConstElementPtr json;
            json = parser.parseFile(config_file, Parser6Context::PARSER_DHCP6);
            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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

            // Check the logic next.
            ConstElementPtr dhcp6 = json->get("Dhcp6");
            if (!dhcp6) {
                cerr << "Missing mandatory Dhcp6 element" << endl;
                return (EXIT_FAILURE);
            }
            ControlledDhcpv6Srv server(0);
            ConstElementPtr answer;

            // Now we pass the Dhcp6 configuration to the server, but
            // tell it to check the configuration only (check_only = true)
            answer = configureDhcp6Server(server, dhcp6, 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);
            }


Francis Dupont's avatar
Francis Dupont committed
176 177 178 179 180 181
            return (EXIT_SUCCESS);
        } catch (const std::exception& ex) {
            cerr << "Syntax check failed with " << ex.what() << endl;
        }
        return (EXIT_FAILURE);
    }
182

183
    int ret = EXIT_SUCCESS;
184
    try {
185 186 187 188 189
        // 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.
        CfgMgr::instance().setDefaultLoggerName(DHCP6_LOGGER_NAME);

190
        // Initialize logging.  If verbose, we'll use maximum verbosity.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
191
        Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
192 193

        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
194
            .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no");
195

196
        LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
197

Tomek Mrugalski's avatar
Tomek Mrugalski committed
198
        // Create the server instance.
199
        ControlledDhcpv6Srv server(port_number);
200

Tomek Mrugalski's avatar
Tomek Mrugalski committed
201 202 203
        // Remember verbose-mode
        server.setVerbose(verbose_mode);

204 205 206 207 208
        // Create our PID file
        server.setProcName(DHCP6_NAME);
        server.setConfigFile(config_file);
        server.createPIDFile();

Tomek Mrugalski's avatar
Tomek Mrugalski committed
209
        try {
210
            // Initialize the server, e.g. establish control session
211
            // Read a configuration file
Tomek Mrugalski's avatar
Tomek Mrugalski committed
212
            server.init(config_file);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
213

Tomek Mrugalski's avatar
Tomek Mrugalski committed
214
        } catch (const std::exception& ex) {
215

Tomek Mrugalski's avatar
Tomek Mrugalski committed
216 217 218 219 220 221
            try {
                // Let's log out what went wrong.
                isc::log::LoggerManager log_manager;
                log_manager.process();
                LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
            } catch (...) {
Andrei Pavel's avatar
Andrei Pavel committed
222 223 224
                // 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
225 226
                cerr << "Failed to initialize server: " << ex.what() << endl;
            }
227

Tomek Mrugalski's avatar
Tomek Mrugalski committed
228
            return (EXIT_FAILURE);
229
        }
230

231 232 233
        // Tell the admin we are ready to process packets
        LOG_INFO(dhcp6_logger, DHCP6_STARTED).arg(VERSION);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
234
        // And run the main loop of the server.
235
        server.run();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
236

237
        LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
238

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

        // First, we print the error on stderr (that should always work)
        cerr << DHCP6_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)
263 264 265 266 267
        try {
            LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
268
        ret = EXIT_FAILURE;
269 270 271 272
    }

    return (ret);
}