/*-
 * redshift-ng - Automatically adjust display colour temperature according the Sun
 * 
 * Copyright (c) 2009-2018        Jon Lund Steffensen <jonlst@gmail.com>
 * Copyright (c) 2014-2016, 2025  Mattias Andrée <m@maandree.se>
 * 
 * redshift-ng is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * redshift-ng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with redshift-ng.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "common.h"


volatile sig_atomic_t exiting = 0;
volatile sig_atomic_t disable = 0;
volatile enum signals signals = 0;


/**
 * Signal handler for exit signals (SIGINT, SIGTERM, SIGQUIT)
 * 
 * @param  signo  The received signal
 */
static void
sigexit(int signo)
{
	exiting = 1;
#ifdef WINDOWS
	signal(signo, &sigexit);
#endif
	(void) signo;
}


#ifndef WINDOWS

/**
 * Signal handler for disable signal (SIGUSR1)
 * 
 * @param  signo  The received signal
 */
static void
sigdisable(int signo)
{
	disable = 1;
	(void) signo;
}


/**
 * Signal handler for forceful exiting; installed by
 * `install_forceful_exit_signal_handlers`
 * 
 * @param  signo  The received signal
 */
static void
sigalrm(int signo)
{
	if (exiting || signo == SIGALRM)
		exit(0);
	exiting = 1;
	alarm(1U);
}


/**
 * Signal handler for SIGUSR2
 * 
 * @param  signo  The received signal
 * @param  info   The received signal data
 * @param  uctx   Interrupted stack context
 */
static void
sigipc(int signo, siginfo_t *info, void *uctx)
{
	int set, mask;
	sigset_t sigusr2_mask;

	(void) signo;
	(void) uctx;

	if (info->si_code != SI_QUEUE)
		return;

	switch (info->si_value.sival_int) {
	case 1:
	case 2:
		mask = 3 << 1;
		break;

	case 5:
	case 6:
		mask = 3 << 5;
		break;

	case 7:
	case 8:
		mask = 3 << 7;
		break;

	case 11:
	case 12:
		mask = 3 << 11;
		break;

	case 0:
	case 3:
	case 4:
	case 9:
	case 10:
		mask = 0;
		break;

	default:
		return;
	}

	signals |= set = 1 << info->si_value.sival_int;
	signals &= ~mask | set;

	if (set == SIGNAL_ORDER_BARRIER) {
		sigemptyset(&sigusr2_mask);
		sigaddset(&sigusr2_mask, SIGUSR2);
		if (sigprocmask(SIG_BLOCK, &sigusr2_mask, NULL))
			eprintf("sigprocmask:");
	}
}

#endif


void
install_signal_handlers(void)
{
#ifdef WINDOWS
	if (signal(SIGINT, &sigexit) == SIG_ERR)
		eprintf("signal SIGINT <function pointer>:");
	if (signal(SIGTERM, &sigexit) == SIG_ERR)
		eprintf("signal SIGTERM <function pointer>:");

#else
	struct sigaction sigact;
	sigset_t sigset;

	memset(&sigact, 0, sizeof(sigact));
	sigemptyset(&sigset);
	sigact.sa_mask = sigset;

	sigact.sa_flags = SA_NODEFER;

	sigact.sa_handler = &sigexit;
	if (sigaction(SIGINT, &sigact, NULL))
		eprintf("sigaction SIGINT &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=SA_NODEFER} NULL:");
	if (sigaction(SIGTERM, &sigact, NULL))
		eprintf("sigaction SIGTERM &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=SA_NODEFER} NULL:");
	if (sigaction(SIGQUIT, &sigact, NULL))
		eprintf("sigaction SIGQUIT &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=SA_NODEFER} NULL:");

	sigact.sa_handler = &sigdisable;
	if (sigaction(SIGUSR1, &sigact, NULL))
		eprintf("sigaction SIGUSR1 &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=SA_NODEFER} NULL:");

	sigact.sa_flags = 0;

	sigact.sa_handler = SIG_IGN; /* cause child processes (hooks) to be reaped automatically */
	if (sigaction(SIGCHLD, &sigact, NULL))
		eprintf("sigaction SIGCHLD &{.sa_handler=SIG_IGN, .sa_mask={}, .sa_flags=0} NULL:");

	sigact.sa_flags = SA_SIGINFO;

	sigact.sa_sigaction = &sigipc;
	if (sigaction(SIGUSR2, &sigact, NULL))
		eprintf("sigaction SIGUSR2 &{.sa_sigaction=<function pointer>, .sa_mask={}, .sa_flags=SA_SIGINFO} NULL:");
#endif
}


#ifndef WINDOWS
void
install_forceful_exit_signal_handlers(void)
{
	struct sigaction sigact;
	sigset_t sigset;

	exiting = 0;
	memset(&sigact, 0, sizeof(sigact));
	sigemptyset(&sigset);
	sigact.sa_mask = sigset;
	sigact.sa_flags = 0;
	sigact.sa_handler = &sigalrm;
	if (sigaction(SIGINT, &sigact, NULL))
		eprintf("sigaction SIGINT &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=0} NULL:");
	if (sigaction(SIGTERM, &sigact, NULL))
		eprintf("sigaction SIGTERM &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=0} NULL:");
	if (sigaction(SIGQUIT, &sigact, NULL))
		eprintf("sigaction SIGQUIT &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=0} NULL:");
	if (sigaction(SIGALRM, &sigact, NULL))
		eprintf("sigaction SIGALRM &{.sa_handler=<function pointer>, .sa_mask={}, .sa_flags=0} NULL:");
}
#endif