#ifndef __CR_PARASITE_H__
#define __CR_PARASITE_H__

#define PARASITE_MAX_SIZE	(64 << 10)

#ifndef __ASSEMBLY__

#include <sys/un.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>

#include "image.h"
#include "util-pie.h"
#include "common/lock.h"
#include "infect-rpc.h"

#include "images/vma.pb-c.h"
#include "images/tty.pb-c.h"

#define __head __used __section(.head.text)

enum {
	PARASITE_CMD_DUMP_THREAD = PARASITE_USER_CMDS,
	PARASITE_CMD_MPROTECT_VMAS,
	PARASITE_CMD_DUMPPAGES,

	PARASITE_CMD_DUMP_SIGACTS,
	PARASITE_CMD_DUMP_ITIMERS,
	PARASITE_CMD_DUMP_POSIX_TIMERS,
	PARASITE_CMD_DUMP_MISC,
	PARASITE_CMD_DRAIN_FDS,
	PARASITE_CMD_GET_PROC_FD,
	PARASITE_CMD_DUMP_TTY,
	PARASITE_CMD_CHECK_VDSO_MARK,
	PARASITE_CMD_CHECK_AIOS,
	PARASITE_CMD_DUMP_CGROUP,

	PARASITE_CMD_MAX,
};

struct parasite_vma_entry
{
	unsigned long	start;
	unsigned long	len;
	int		prot;
};

struct parasite_vdso_vma_entry {
	unsigned long	start;
	unsigned long	len;
	unsigned long	proxy_vdso_addr;
	unsigned long	proxy_vvar_addr;
	int		is_marked;
	bool		try_fill_symtable;
	bool		is_vdso;
};

struct parasite_dump_pages_args {
	unsigned int	nr_vmas;
	unsigned int	add_prot;
	unsigned int	off;
	unsigned int	nr_segs;
	unsigned int	nr_pages;
};

static inline struct parasite_vma_entry *pargs_vmas(struct parasite_dump_pages_args *a)
{
	return (struct parasite_vma_entry *)(a + 1);
}

static inline struct iovec *pargs_iovs(struct parasite_dump_pages_args *a)
{
	return (struct iovec *)(pargs_vmas(a) + a->nr_vmas);
}

struct parasite_dump_sa_args {
	rt_sigaction_t sas[SIGMAX];
};

struct parasite_dump_itimers_args {
	struct itimerval real;
	struct itimerval virt;
	struct itimerval prof;
};

struct posix_timer {
	int it_id;
	struct itimerspec val;
	int overrun;
};

struct parasite_dump_posix_timers_args {
	int timer_n;
	struct posix_timer timer[0];
};

struct parasite_aio {
	unsigned long ctx;
	unsigned int size;
};

struct parasite_check_aios_args {
	unsigned nr_rings;
	struct parasite_aio ring[0];
};

static inline int posix_timers_dump_size(int timer_n)
{
	return sizeof(int) + sizeof(struct posix_timer) * timer_n;
}

/*
 * Misc sfuff, that is too small for separate file, but cannot
 * be read w/o using parasite
 */

struct parasite_dump_misc {
	unsigned long		brk;

	u32 pid;
	u32 sid;
	u32 pgid;
	u32 umask;

	int dumpable;
};

/*
 * Calculate how long we can make the groups array in parasite_dump_creds
 * and still fit the struct in one page
 */
#define PARASITE_MAX_GROUPS							\
	((PAGE_SIZE - sizeof(struct parasite_dump_thread) -			\
	 offsetof(struct parasite_dump_creds, groups)) / sizeof(unsigned int)) /* groups */

struct parasite_dump_creds {
	unsigned int		cap_last_cap;

	u32			cap_inh[CR_CAP_SIZE];
	u32			cap_prm[CR_CAP_SIZE];
	u32			cap_eff[CR_CAP_SIZE];
	u32			cap_bnd[CR_CAP_SIZE];

	int			uids[4];
	int			gids[4];
	unsigned int		secbits;
	unsigned int		ngroups;
	/*
	 * FIXME -- this structure is passed to parasite code
	 * through parasite args area so in parasite_dump_creds()
	 * call we check for size of this data fits the size of
	 * the area. Unfortunatelly, we _actually_ use more bytes
	 * than the sizeof() -- we put PARASITE_MAX_GROUPS int-s
	 * in there, so the size check is not correct.
	 *
	 * However, all this works simply because we make sure
	 * the PARASITE_MAX_GROUPS is so, that the total amount
	 * of memory in use doesn't exceed the PAGE_SIZE and the
	 * args area is at least one page (PARASITE_ARG_SIZE_MIN).
	 */
	unsigned int		groups[0];
};

struct parasite_dump_thread {
	unsigned int			*tid_addr;
	pid_t				tid;
	tls_t				tls;
	stack_t				sas;
	int				pdeath_sig;
	struct parasite_dump_creds	creds[0];
};

static inline void copy_sas(ThreadSasEntry *dst, const stack_t *src)
{
	dst->ss_sp = encode_pointer(src->ss_sp);
	dst->ss_size = (u64)src->ss_size;
	dst->ss_flags = src->ss_flags;
}

/*
 * How many descriptors can be transferred from parasite:
 *
 * 1) struct parasite_drain_fd + all descriptors should fit into one page
 * 2) The value should be a multiple of CR_SCM_MAX_FD, because descriptors
 *    are transferred with help of send_fds and recv_fds.
 * 3) criu should work with a defaul value of the file limit (1024)
 */
#define PARASITE_MAX_FDS	CR_SCM_MAX_FD * 3

struct parasite_drain_fd {
	int	nr_fds;
	int	fds[0];
};

struct fd_opts {
	char flags;
	struct {
		uint32_t uid;
		uint32_t euid;
		uint32_t signum;
		uint32_t pid_type;
		uint32_t pid;
	} fown;
};

static inline int drain_fds_size(struct parasite_drain_fd *dfds)
{
	int nr_fds = min((int)PARASITE_MAX_FDS, dfds->nr_fds);
	return sizeof(*dfds) + nr_fds * (sizeof(dfds->fds[0]) + sizeof(struct fd_opts));
}

struct parasite_tty_args {
	int	fd;
	int	type;

	int	sid;
	int	pgrp;
	bool	hangup;

	int	st_pckt;
	int	st_lock;
	int	st_excl;
};

struct parasite_dump_cgroup_args {
	/*
	 * 4K should be enough for most cases.
	 *
	 * The string is null terminated.
	 */
	char contents[1 << 12];
};

#endif /* !__ASSEMBLY__ */

#endif /* __CR_PARASITE_H__ */
