main.cc 9.13 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 11
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
Francis Dupont's avatar
Francis Dupont committed
12
#include <dhcp6/parser_context.h>
13
#include <dhcp6/json_config_parser.h>
14
#include <dhcpsrv/cfgmgr.h>
15
#include <log/logger_support.h>
16
#include <log/logger_manager.h>
17
#include <exceptions/exceptions.h>
18
#include <cfgrpt/config_report.h>
19
#include <process/daemon.h>
20

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

#include <iostream>

Francis Dupont's avatar
Francis Dupont committed
25
using namespace isc::data;
26
using namespace isc::dhcp;
27
using namespace isc::process;
28
using namespace std;
29

30
/// This file contains entry point (main() function) for standard DHCPv6 server
31
/// component of Kea software suite. It parses command-line arguments and
32 33 34 35 36 37 38
/// 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.

39
namespace {
40
const char* const DHCP6_NAME = "kea-dhcp6";
41

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

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

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

73 74 75
    // The standard config file
    std::string config_file("");

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

82
        case 'v':
83
            cout << Dhcpv6Srv::getVersion(false) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
84
            return (EXIT_SUCCESS);
85 86

        case 'V':
87
            cout << Dhcpv6Srv::getVersion(true) << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
88
            return (EXIT_SUCCESS);
89

90 91 92 93
        case 'W':
            cout << isc::detail::getConfigReport() << endl;
            return (EXIT_SUCCESS);

94 95 96 97
        case 't':
            check_mode = true;
            // falls through

98 99 100 101
        case 'c': // config file
            config_file = optarg;
            break;

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

117 118 119 120 121
        default:
            usage();
        }
    }

122 123 124 125 126
    // Check for extraneous parameters.
    if (argc > optind) {
        usage();
    }

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

133 134 135
    // This is the DHCPv6 server
    CfgMgr::instance().setFamily(AF_INET6);

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

            // Check the syntax first.
Francis Dupont's avatar
Francis Dupont committed
145 146 147 148 149 150 151 152 153 154
            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;
            }
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

            // 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
179 180 181 182 183 184
            return (EXIT_SUCCESS);
        } catch (const std::exception& ex) {
            cerr << "Syntax check failed with " << ex.what() << endl;
        }
        return (EXIT_FAILURE);
    }
185

186
    int ret = EXIT_SUCCESS;
187
    try {
188 189 190
        // 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.
191
        Daemon::setDefaultLoggerName(DHCP6_LOGGER_NAME);
192

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

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

199
        LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
200

Tomek Mrugalski's avatar
Tomek Mrugalski committed
201
        // Create the server instance.
202
        ControlledDhcpv6Srv server(port_number);
203

Tomek Mrugalski's avatar
Tomek Mrugalski committed
204 205 206
        // Remember verbose-mode
        server.setVerbose(verbose_mode);

207 208 209 210 211
        // Create our PID file
        server.setProcName(DHCP6_NAME);
        server.setConfigFile(config_file);
        server.createPIDFile();

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
219 220 221 222 223 224
            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
225 226 227
                // 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
228 229
                cerr << "Failed to initialize server: " << ex.what() << endl;
            }
230

Tomek Mrugalski's avatar
Tomek Mrugalski committed
231
            return (EXIT_FAILURE);
232
        }
233

234 235 236
        // Tell the admin we are ready to process packets
        LOG_INFO(dhcp6_logger, DHCP6_STARTED).arg(VERSION);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
237
        // And run the main loop of the server.
238
        server.run();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
239

240
        LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
241

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

        // 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)
266 267 268 269 270
        try {
            LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what());
        } catch (...) {
            // Already logged so ignore
        }
271
        ret = EXIT_FAILURE;
272 273 274 275
    }

    return (ret);
}