/* NHRP daemon main functions
 * Copyright (c) 2014-2015 Timo Teräs
 *
 * This file is free software: you may copy, redistribute and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 */

#include <unistd.h>
#include <libgen.h>

#include "zebra.h"
#include "privs.h"
#include "getopt.h"
#include "thread.h"
#include "sigevent.h"
#include "version.h"
#include "log.h"
#include "memory.h"
#include "command.h"

#include "nhrpd.h"
#include "netlink.h"

unsigned int debug_flags = 0;

struct thread_master *master;
struct timeval current_time;
static const char *pid_file = PATH_NHRPD_PID;
static char config_default[] = SYSCONFDIR NHRP_DEFAULT_CONFIG;
static char *config_file = NULL;
static char *vty_addr = NULL;
static int vty_port = NHRP_VTY_PORT;
static int do_daemonise = 0;

/* nhrpd options. */
struct option longopts[] = {
	{ "daemon",      no_argument,       NULL, 'd'},
	{ "config_file", required_argument, NULL, 'f'},
	{ "pid_file",    required_argument, NULL, 'i'},
	{ "socket",      required_argument, NULL, 'z'},
	{ "help",        no_argument,       NULL, 'h'},
	{ "vty_addr",    required_argument, NULL, 'A'},
	{ "vty_port",    required_argument, NULL, 'P'},
	{ "user",        required_argument, NULL, 'u'},
	{ "group",       required_argument, NULL, 'g'},
	{ "version",     no_argument,       NULL, 'v'},
	{ 0 }
};

/* nhrpd privileges */
static zebra_capabilities_t _caps_p [] = {
	ZCAP_NET_RAW,
	ZCAP_NET_ADMIN,
	ZCAP_DAC_OVERRIDE,	/* for now needed to write to /proc/sys/net/ipv4/<if>/send_redirect */
};

static struct zebra_privs_t nhrpd_privs = {
#ifdef QUAGGA_USER
	.user = QUAGGA_USER,
#endif
#ifdef QUAGGA_GROUP
	.group = QUAGGA_GROUP,
#endif
#ifdef VTY_GROUP
	.vty_group = VTY_GROUP,
#endif
	.caps_p = _caps_p,
	.cap_num_p = ZEBRA_NUM_OF(_caps_p),
};

static void usage(const char *progname, int status)
{
	if (status != 0)
		fprintf(stderr, "Try `%s --help' for more information.\n", progname);
	else
		printf(
"Usage : %s [OPTION...]\n\
Daemon which manages NHRP protocol.\n\n\
-d, --daemon       Runs in daemon mode\n\
-f, --config_file  Set configuration file name\n\
-i, --pid_file     Set process identifier file name\n\
-z, --socket       Set path of zebra socket\n\
-A, --vty_addr     Set vty's bind address\n\
-P, --vty_port     Set vty's port number\n\
-u, --user         User to run as\n\
-g, --group        Group to run as\n\
-v, --version      Print program version\n\
-h, --help         Display this help and exit\n\
\n\
Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);

	exit(status);
}

static void parse_arguments(const char *progname, int argc, char **argv)
{
	int opt;

	while (1) {
		opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0);
		if(opt < 0) break;

		switch (opt) {
		case 0:
			break;
		case 'd':
			do_daemonise = -1;
			break;
		case 'f':
			config_file = optarg;
			break;
		case 'i':
			pid_file = optarg;
			break;
		case 'z':
			zclient_serv_path_set(optarg);
			break;
		case 'A':
			vty_addr = optarg;
			break;
		case 'P':
			vty_port = atoi (optarg);
			if (vty_port <= 0 || vty_port > 0xffff)
				vty_port = NHRP_VTY_PORT;
			break;
		case 'u':
			nhrpd_privs.user = optarg;
			break;
		case 'g':
			nhrpd_privs.group = optarg;
			break;
		case 'v':
			print_version(progname);
			exit(0);
			break;
		case 'h':
			usage(progname, 0);
			break;
		default:
			usage(progname, 1);
			break;
		}
	}
}

static void nhrp_sigusr1(void)
{
	zlog_rotate(NULL);
}

static void nhrp_request_stop(void)
{
	debugf(NHRP_DEBUG_COMMON, "Exiting...");

	nhrp_shortcut_terminate();
	nhrp_nhs_terminate();
	nhrp_zebra_terminate();
	vici_terminate();
	evmgr_terminate();
	nhrp_vc_terminate();
	vrf_terminate();
	/* memory_terminate(); */
	/* vty_terminate(); */
	cmd_terminate();
	/* signal_terminate(); */
	zprivs_terminate(&nhrpd_privs);

	debugf(NHRP_DEBUG_COMMON, "Remove pid file.");
	if (pid_file) unlink(pid_file);
	debugf(NHRP_DEBUG_COMMON, "Done.");

	closezlog(zlog_default);

	exit(0);
}

static struct quagga_signal_t sighandlers[] = {
	{ .signal = SIGUSR1, .handler = &nhrp_sigusr1, },
	{ .signal = SIGINT,  .handler = &nhrp_request_stop, },
	{ .signal = SIGTERM, .handler = &nhrp_request_stop, },
};

int main(int argc, char **argv)
{
	const char *progname;

	/* Set umask before anything for security */
	umask(0027);
	progname = basename(argv[0]);
	zlog_default = openzlog(progname, ZLOG_NHRP, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
	zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING);

	parse_arguments(progname, argc, argv);

	/* Library inits. */
	master = thread_master_create();
	zprivs_init(&nhrpd_privs);
	signal_init(master, array_size(sighandlers), sighandlers);
	cmd_init(1);
	vty_init(master);
	memory_init();
	nhrp_interface_init();
	vrf_init();
	resolver_init();

	/* Run with elevated capabilities, as for all netlink activity
	 * we need privileges anyway. */
	nhrpd_privs.change(ZPRIVS_RAISE);

	netlink_init();
	evmgr_init();
	nhrp_vc_init();
	nhrp_packet_init();
	vici_init();
	nhrp_zebra_init();
	nhrp_shortcut_init();

	nhrp_config_init();

	/* Get zebra configuration file. */
	zlog_set_level(NULL, ZLOG_DEST_STDOUT, do_daemonise ? ZLOG_DISABLED : LOG_DEBUG);
	vty_read_config(config_file, config_default);

	if (do_daemonise && daemon(0, 0) < 0) {
		zlog_err("daemonise: %s", safe_strerror(errno));
		exit (1);
	}

	/* write pid file */
	if (pid_output(pid_file) < 0) {
		zlog_err("error while writing pidfile");
		exit (1);
	}

	/* Create VTY socket */
	vty_serv_sock(vty_addr, vty_port, NHRP_VTYSH_PATH);
	zlog_notice("nhrpd starting: vty@%d", vty_port);

	/* Main loop */
	thread_main (master);

	return 0;
}
