/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "signal_handler.h"

#include "util.h"

/*
 * si_syscall was added in glibc-2.17+, but Android still uses glibc-2.15
 * for its prebuilt binary host toolchains.  Add a compat hack for it.
 */
static int get_si_syscall(const siginfo_t *info)
{
#if defined(si_syscall)
	return info->si_syscall;
#endif

	typedef struct {
		void		*ip;
		int		nr;
		unsigned int	arch;
	} local_siginfo_t;

	union {
		const siginfo_t *info;
		const local_siginfo_t *local_info;
	} local_info = {
		.info = info,
	};
	return local_info.local_info->nr;
}

void log_sigsys_handler(int sig attribute_unused, siginfo_t *info,
			void *void_context attribute_unused)
{
	const char *syscall_name;
	int nr = get_si_syscall(info);
	syscall_name = lookup_syscall_name(nr);

	if (syscall_name)
		die("blocked syscall: %s", syscall_name);
	else
		die("blocked syscall: %d", nr);

	/*
	 * We trapped on a syscall that should have killed the process.
	 * This should never ever return, but we're paranoid.
	 */
	for (;;)
		_exit(1);
}

int install_sigsys_handler()
{
	int ret = 0;
	struct sigaction act;
	sigset_t mask;

	memset(&act, 0, sizeof(act));
	act.sa_sigaction = &log_sigsys_handler;
	act.sa_flags = SA_SIGINFO;

	sigemptyset(&mask);
	sigaddset(&mask, SIGSYS);

	ret = sigaction(SIGSYS, &act, NULL);
	if (ret < 0)
		return ret;

	ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
	if (ret < 0)
		return ret;

	return 0;
}