
/*
 * Copyright 2011-2022 Cuberite Contributors
 *
 * 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.
 */

// ConsoleSignalHandler.h

// Intercepts signals for graceful CTRL-C (and others) handling.

// This file MUST NOT be included from anywhere other than main.cpp.





#include <csignal>





// Because SIG_DFL or SIG_IGN could be NULL instead of nullptr, we need to disable the Clang warning here:
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif

static void NonCtrlHandler(int a_Signal)
{
	LOGD("Terminate event raised from std::signal");

	switch (a_Signal)
	{
		case SIGSEGV:
		{
			PrintStackTrace();

			LOGERROR(
				"Failure report: \n\n"
				"  :(   | Cuberite has encountered an error and needs to close\n"
				"       | SIGSEGV: Segmentation fault\n"
				"       |\n"
#ifdef BUILD_ID
				"       | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID ")\n"
				"       | from commit " BUILD_COMMIT_ID "\n"
#endif
			);

			std::signal(SIGSEGV, SIG_DFL);
			return;
		}
		case SIGABRT:
#ifdef SIGABRT_COMPAT
		case SIGABRT_COMPAT:
#endif
		{
			PrintStackTrace();

			LOGERROR(
				"Failure report: \n\n"
				"  :(   | Cuberite has encountered an error and needs to close\n"
				"       | SIGABRT: Server self-terminated due to an internal fault\n"
				"       |\n"
#ifdef BUILD_ID
				"       | Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID ")\n"
				"       | from commit " BUILD_COMMIT_ID "\n"
#endif
			);

			std::signal(SIGSEGV, SIG_DFL);
			return;
		}
		case SIGINT:
		case SIGTERM:
		{
			// Server is shutting down, wait for it...
			cRoot::Stop();
			return;
		}
	}
}

#ifdef __clang__
#pragma clang diagnostic pop
#endif





#ifdef _WIN32

/** Handle CTRL events in windows, including console window close. */
static BOOL CtrlHandler(DWORD fdwCtrlType)
{
	cRoot::Stop();
	LOGD("Terminate event raised from the Windows CtrlHandler");

	// Delay as much as possible to try to get the server to shut down cleanly - 10 seconds given by Windows:
	std::this_thread::sleep_for(std::chrono::seconds(10));

	// Returning from main() automatically aborts this handler thread.

	return TRUE;
}

#endif





namespace ConsoleSignalHandler
{
	static void Register()
	{
		std::signal(SIGSEGV, NonCtrlHandler);
		std::signal(SIGTERM, NonCtrlHandler);
		std::signal(SIGINT, NonCtrlHandler);
		std::signal(SIGABRT, NonCtrlHandler);
#ifdef SIGABRT_COMPAT
		std::signal(SIGABRT_COMPAT, NonCtrlHandler);
#endif
#ifdef SIGPIPE
		std::signal(SIGPIPE, SIG_IGN);  // Ignore (PR #2487).
#endif

#ifdef _WIN32
		SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(CtrlHandler), TRUE);
#endif
	}
};
