/*
* Copyright [2021] JD.com, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>

#include "../config/config.h"
#include "../table/table_def.h"
#include "table/hotbackup_table_def.h"
#include "daemon/daemon.h"
#include "../log/log.h"
#include <pthread.h>
#include "config/dbconfig.h"
#include "version.h"
#include "table/table_def_manager.h"

DTCConfig *g_dtc_config = NULL;
DTCTableDefinition *g_table_def[2] = { NULL, NULL };
DbConfig *dbConfig = NULL;

volatile int stop = 0;
volatile int crash_signo = 0;
int background = 1;
extern const char project_name[];
extern const char usage_argv[];

pthread_t mainthreadid;

bool flatMode;
//打印版本信息
void show_version_detail()
{
	printf("%s version: %s\n", project_name, version_detail);
}
//打印编译信息
void show_comp_date()
{
	printf("%s compile date: %s %s\n", project_name, compdatestr,
	       comptimestr);
}

void show_usage()
{
	show_version_detail();
	show_comp_date();
	printf("Usage:\n    %s  [-d] [-h] [-v] [-V]%s\n", project_name,
	       usage_argv);
}

//获取初始信息（输入参数、编译信息等）
int load_entry_parameter(int argc, char **argv)
{
	int c;

	while ((c = getopt(argc, argv, "df:t:hvV")) != -1) {
		switch (c) {
		case 'd':
			background = 0;
			break;
		case 'f':
			strncpy(cache_file, optarg, sizeof(cache_file) - 1);
			break;
		case 't':
			strncpy(table_file, optarg, sizeof(table_file) - 1);
			break;
		case 'h':
			show_usage();
			exit(0);
		case 'v':
			show_version_detail();
			show_comp_date();
			exit(0);
		case 'V':
			show_version_detail();
			show_comp_date();
			exit(0);
		case '?':
			show_usage();
			exit(-1);
		}
	}

	//init_log("dtcd");
	log4cplus_info("%s v%s: starting....", project_name, version);
	strcpy(table_file, "/etc/dtc/table.yaml");
	strcpy(cache_file, "/etc/dtc/dtc.yaml");
	g_dtc_config = new DTCConfig;
	//load config file and copy it to ../stat
	if (g_dtc_config->parse_config(table_file, "DATABASE_CONF", true) == -1)
		return -1;

	if (g_dtc_config->parse_config(cache_file, "cache", true))
		return -1;

	dbConfig = DbConfig::Load(g_dtc_config);
	if (dbConfig == NULL)
		return -1;

	g_table_def[0] = dbConfig->build_table_definition();
	if (g_table_def[0] == NULL)
		return -1;

	DTCTableDefinition *t;
	if ((t = TableDefinitionManager::instance()->load_table(table_file)) ==
	    NULL)
		return -1;
	TableDefinitionManager::instance()->set_cur_table_def(t, 0);

	g_table_def[1] = build_hot_backup_table();
	if (g_table_def[1] == NULL)
		return -1;

	if (!TableDefinitionManager::instance()->build_hot_backup_table_def())
		return -1;

	return 0;
}

static void sigterm_handler(int signo)
{
	stop = 1;
}
//信号量初始化，设置后台运行进程
int init_daemon()
{
	struct sigaction sa;
	sigset_t sset;

	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sigterm_handler;
	sigaction(SIGINT, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGQUIT, &sa, NULL);
	sigaction(SIGHUP, &sa, NULL);

	signal(SIGPIPE, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	/********************************
 * sigemptyset:清空信号集
 * sigaddset：将信号加入信号集
 * sigprocmask：查询或设置信号屏蔽字
 *********************************/
	sigemptyset(&sset);
	sigaddset(&sset, SIGTERM);
	sigaddset(&sset, SIGSEGV);
	sigaddset(&sset, SIGBUS);
	sigaddset(&sset, SIGABRT);
	sigaddset(&sset, SIGILL);
	sigaddset(&sset, SIGCHLD);
	sigaddset(&sset, SIGFPE);
	sigprocmask(SIG_UNBLOCK, &sset, &sset);

	int ret = background ? daemon(1, 1) : 0;
	mainthreadid = pthread_self();
	return ret;
}

void DaemonCrashed(int signo)
{
	stop = 1;
	crash_signo = signo;
	if (mainthreadid)
		pthread_kill(mainthreadid, SIGTERM);
}

void daemon_cleanup(void)
{
#if MEMCHECK
	DELETE(gBConfig);
	DELETE(gConfig);
	DELETE(g_table_def[0]);
	DELETE(g_table_def[1]);
	if (dbConfig)
		dbConfig->destory();
#endif
	// CrashProtect: special retval 85 means protected-crash
	if (crash_signo)
		exit(85);
}

int daemon_get_fd_limit(void)
{
	struct rlimit rlim;
	if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
		log4cplus_info("Query FdLimit failed, errmsg[%s]",
			       strerror(errno));
		return -1;
	}

	return rlim.rlim_cur;
}

int daemon_set_fd_limit(int maxfd)
{
	struct rlimit rlim;
	if (maxfd) {
		/* raise open files */
		rlim.rlim_cur = maxfd;
		rlim.rlim_max = maxfd;
		if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
			log4cplus_info(
				"Increase FdLimit failed, set val[%d] errmsg[%s]",
				maxfd, strerror(errno));
			return -1;
		}
	}
	return 0;
}

//core(coredump)文件初始化
int init_core_dump(void)
{
	struct rlimit rlim;

	/* allow core dump  100M */
	rlim.rlim_cur = 100UL << 20;
	rlim.rlim_max = 100UL << 20;
	//设置core文件的最大字节数
	if (setrlimit(RLIMIT_CORE, &rlim) == -1) {
		//如果设置失败，将软限制设置为硬限制，并重新设置
		if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
			rlim.rlim_cur = rlim.rlim_max;
			setrlimit(RLIMIT_CORE, &rlim);
		}

		log4cplus_info("EnableCoreDump size[%ld]", (long)rlim.rlim_max);
	}

	return 0;
}
