/*
 * sydbox/pink.c
 *
 * pinktrace wrapper functions
 *
 * Copyright (c) 2013 Ali Polatel <alip@exherbo.org>
 * Released under the terms of the 3-clause BSD license
 */

#include "sydbox.h"
#include <errno.h>
#include <string.h>
#include <pinktrace/pink.h>
#include "log.h"

int syd_trace_detach(syd_proc_t *current, int sig)
{
	int r;

	assert(current);

	r = pink_trace_detach(current->pid, sig);
	if (r == 0)
		log_trace("DETACH sig:%d", sig);
	else if (r == -ESRCH)
		err_trace(-r, "trace_detach(sig:%d) failed", sig);
	else
		err_warning(-r, "trace_detach(sig:%d) failed", sig);

	ignore_proc(current);
	return r;
}

int syd_trace_kill(syd_proc_t *current, int sig)
{
	int r;

	assert(current);

	r = pink_trace_kill(current->pid, current->ppid, sig);
	if (r == 0)
		log_trace("KILL sig:%d", sig);
	else if (r == -ESRCH)
		err_trace(-r, "trace_kill(sig:%d) failed", sig);
	else
		err_warning(-r, "trace_kill(sig:%d) failed", sig);

	ignore_proc(current);
	return r;
}

int syd_trace_setup(syd_proc_t *current)
{
	int r;
	int opts = sydbox->trace_options;

	assert(current);

	log_trace("setting trace options 0x%x", opts);
	r = pink_trace_setup(current->pid, opts);
	if (r == -ESRCH)
		err_trace(-r, "trace_setup() failed");
	else if (r < 0)
		err_warning(-r, "trace_setup() failed");
	return r;
}

int syd_trace_geteventmsg(syd_proc_t *current, unsigned long *data)
{
	int r;

	assert(current);

	r = pink_trace_geteventmsg(current->pid, data);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "trace_geteventmsg() failed");
	else if (r < 0)
		err_warning(-r, "trace_geteventmsg() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_regset_fill(syd_proc_t *current)
{
	int r;

	assert(current);

	r = pink_regset_fill(current->pid, current->regset);
	if (r == 0) {
		pink_read_abi(current->pid, current->regset, &current->abi);
		return 0;
	} else if (r == -ESRCH) {
		err_trace(-r, "regset_fill() failed");
	} else if (r < 0) {
		err_warning(-r, "regset_fill() failed");
	}
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_syscall(syd_proc_t *current, long *sysnum)
{
	int r;

	assert(current);
	assert(sysnum);

	r = pink_read_syscall(current->pid, current->regset, sysnum);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_syscall() failed");
	else if (r < 0)
		err_warning(-r, "read_syscall() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_retval(syd_proc_t *current, long *retval, int *error)
{
	int r;

	assert(current);

	r = pink_read_retval(current->pid, current->regset, retval, error);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_retval() failed");
	else if (r < 0)
		err_warning(-r, "read_retval() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_argument(syd_proc_t *current, unsigned arg_index, long *argval)
{
	int r;

	assert(current);
	assert(argval);

	r = pink_read_argument(current->pid, current->regset, arg_index, argval);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_argument() failed");
	else if (r < 0)
		err_warning(-r, "read_argument() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_argument_int(syd_proc_t *current, unsigned arg_index, int *argval)
{
	int r;
	long arg_l;

	assert(current);
	assert(argval);

	r = pink_read_argument(current->pid, current->regset, arg_index, &arg_l);
	if (r == 0) {
		*argval = (int)arg_l;
		return 0;
	} else if (r == -ESRCH) {
		err_trace(-r, "read_argument_int() failed");
	} else if (r < 0) {
		err_warning(-r, "read_argument_int() failed");
	}
	return (r == -ESRCH) ? -ESRCH : panic(current);

}
ssize_t syd_read_string(syd_proc_t *current, long addr, char *dest, size_t len)
{
	ssize_t r;
	int save_errno;

	assert(current);

	errno = 0;
	r = pink_read_string(current->pid, current->regset, addr, dest, len);
	save_errno = errno;
	if (r < 0) {
		if (save_errno == EFAULT)
			log_trace("read_string() hit NULL pointer");
		else if (save_errno != ESRCH)
			save_errno = panic(current);
		errno = save_errno;
		return -1;
	} else if ((size_t)r <= len) {
		/* partial read? */
		errno = 0;
		dest[r] = '\0';
	}
	return r;
}

int syd_read_socket_argument(syd_proc_t *current, bool decode_socketcall,
			     unsigned arg_index, unsigned long *argval)
{
	int r;

	assert(current);
	assert(argval);

	r = pink_read_socket_argument(current->pid, current->regset, decode_socketcall,
				      arg_index, argval);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_socket_argument() failed");
	else if (r < 0)
		err_warning(-r, "read_socket_argument() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_socket_subcall(syd_proc_t *current, bool decode_socketcall,
			    long *subcall)
{
	int r;

	assert(current);

	r = pink_read_socket_subcall(current->pid, current->regset, decode_socketcall, subcall);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_socket_subcall() failed");
	else if (r < 0)
		err_warning(-r, "read_socket_subcall() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_read_socket_address(syd_proc_t *current, bool decode_socketcall,
			    unsigned arg_index, int *fd,
			    struct pink_sockaddr *sockaddr)
{
	int r;

	assert(current);
	assert(sockaddr);

	r = pink_read_socket_address(current->pid, current->regset, decode_socketcall,
				     arg_index, fd, sockaddr);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "read_socket_address() failed");
	else if (r < 0)
		err_warning(-r, "read_socket_address() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_write_syscall(syd_proc_t *current, long sysnum)
{
	int r;

	assert(current);

	r = pink_write_syscall(current->pid, current->regset, sysnum);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "write_syscall() failed");
	else if (r < 0)
		err_warning(-r, "write_syscall() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}

int syd_write_retval(syd_proc_t *current, long retval, int error)
{
	int r;

	assert(current);

	r = pink_write_retval(current->pid, current->regset, retval, error);
	if (r == 0)
		return 0;
	else if (r == -ESRCH)
		err_trace(-r, "write_retval() failed");
	else if (r < 0)
		err_warning(-r, "write_retval() failed");
	return (r == -ESRCH) ? -ESRCH : panic(current);
}
