/*
 * Copyright 2010-2011 Red Hat, Inc.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License v2 or (at your option) any later version.
 */

#ifndef __SANLOCK_INTERNAL_H__
#define __SANLOCK_INTERNAL_H__

#ifndef GNUC_UNUSED
#define GNUC_UNUSED __attribute__((__unused__))
#endif

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

#ifndef EXTERN
#define EXTERN extern
#else
#undef EXTERN
#define EXTERN
#endif

#include "sanlock.h"
#include "sanlock_rv.h"
#include "sanlock_resource.h"
#include "leader.h"
#include "paxos_dblock.h"
#include "mode_block.h"
#include "rindex_disk.h"
#include "list.h"
#include "monotime.h"
#include "sizeflags.h"

#include <libaio.h>

#define ONE_MB_IN_BYTES 1048576
#define TWO_MB_IN_BYTES 2097152
#define ONE_MB_IN_KB 1024

/* default max number of hosts supported */

#define DEFAULT_MAX_HOSTS 2000

#define LOG_DUMP_SIZE (1024*1024)

/* this is just the path to the executable, not full command line */

#define COMMAND_MAX 4096

#define SANLOCK_RUN_DIR "SANLOCK_RUN_DIR"
#define DEFAULT_RUN_DIR "/run/sanlock"
#define SANLOCK_PRIVILEGED "SANLOCK_PRIVILEGED"

#define SANLK_LOG_DIR "/var/log"
#define SANLK_LOGFILE_NAME "sanlock.log"
#define SANLK_LOCKFILE_NAME "sanlock.pid"
#define SANLK_CONF_PATH "/etc/sanlock/sanlock.conf"

#define DAEMON_NAME "sanlock"


/* for paxos_lease sync_disk + offset:
   points to 1 leader_record + 1 request_record + MAX_HOSTS paxos_dblock's =
   256 blocks = 128KB, ref: lease_item_record */

/* must mirror external sanlk_disk */

struct sync_disk {
	char path[SANLK_PATH_LEN];
	uint64_t offset;
	uint32_t sector_size;	/* sanlk_disk pad1 */
	int fd;			/* sanlk_disk pad2 */
};

struct delta_extra {
	uint64_t field1;
	uint64_t field2;
	uint64_t field3;
};

/*
 * There are two different wrappers around a sanlk_resource:
 * 'struct token' keeps track of resources per-client, client.tokens[]
 * 'struct resource' keeps track of resources globally, resources list
 */

#define T_RESTRICT_SIGKILL	 0x00000001 /* inherited from client->restricted */
#define T_RESTRICT_SIGTERM	 0x00000002 /* inherited from client->restricted */
#define T_RETRACT_PAXOS		 0x00000004
#define T_WRITE_DBLOCK_MBLOCK_SH 0x00000008 /* make paxos layer include mb SHARED with dblock */
#define T_CHECK_EXISTS		 0x00000010 /* make paxos layer not error if reading lease finds none */
#define T_CAW			 0x00000020
#define T_CLEANUP_CONVERT	 0x00000040
#define T_CLEANUP_OWNER		 0x00000080

struct token {
	/* values copied from acquire res arg */
	uint64_t acquire_lver;
	uint64_t acquire_data64;
	uint32_t acquire_data32;
	uint32_t acquire_flags;

	/* copied from the sp with r.lockspace_name */
	uint64_t host_id;
	uint64_t host_generation;
	uint32_t space_id;
	uint32_t io_timeout;

	/* internal */
	struct list_head list; /* resource->tokens */
	struct resource *resource;
	int pid;
	int client_id;
	uint32_t flags;  /* be careful to avoid using this from different threads */
	uint32_t token_id;
	uint32_t res_id;
	int sector_size;
	int align_size;
	int space_dead; /* copied from sp->space_dead, set by main thread */
	int shared_count; /* set during ballot by paxos_lease_acquire */
	char shared_bitmap[HOSTID_BITMAP_SIZE]; /* bit set for host_id with SH */
	int caw_dev_fd;
	char *caw_data;

	struct sync_disk *disks; /* shorthand, points to r.disks[0] */
	struct sanlk_resource r;
	/*
	 * sanlk_resource must be the last element of token.
	 * sanlk_resource ends with sanlk_disk disks[0],
	 * and allocating a token allocates N sanlk_disk structs
	 * after the token struct so they follow the sanlk_resource.
	 */
};

#define R_SHARED     		0x00000001
#define R_THREAD_EXAMINE    	0x00000002
#define R_THREAD_RELEASE	0x00000004
#define R_RESTRICT_SIGKILL	0x00000008 /* inherited from token */
#define R_RESTRICT_SIGTERM	0x00000010 /* inherited from token */
#define R_LVB_WRITE_RELEASE	0x00000020
#define R_UNDO_SHARED		0x00000040
#define R_ERASE_ALL		0x00000080
#define R_CAW			0x00000100
#define R_CLEANUP_CONVERT	0x00000200

#define CAW_STATE_UNKNOWN	0
#define CAW_STATE_USE_DISK	1
#define CAW_STATE_FOUND_DEV	2

struct resource {
	struct list_head list;
	struct list_head tokens;     /* only one token when ex, multiple sh */
	uint64_t host_id;
	uint64_t host_generation;
	uint32_t io_timeout;
	int pid;                     /* copied from token when ex */
	int sector_size;
	int align_size;
	uint32_t res_id;
	uint32_t reused;
	uint32_t flags;
	uint64_t thread_release_retry;
	uint32_t disk_major; /* major:minor of r.disks[0].path */
	uint32_t disk_minor;
	uint32_t caw_state;
	uint32_t caw_major;
	uint32_t caw_minor;
	uint64_t caw_offset;
	char *lvb;
	char *thread_release_caw_data;
	char killpath[SANLK_HELPER_PATH_LEN]; /* copied from client */
	char killargs[SANLK_HELPER_ARGS_LEN]; /* copied from client */
	struct leader_record leader; /* copy of last leader_record we wrote */
	struct paxos_dblock dblock;  /* copy of last paxos_dblock we wrote */
	struct sanlk_resource r;
};

struct lease_status {
	int corrupt_result;
	int acquire_last_result;
	int renewal_last_result;
	uint64_t acquire_last_attempt;
	uint64_t acquire_last_success;
	uint64_t renewal_last_attempt;
	uint64_t renewal_last_success;

	uint32_t renewal_read_count;
	uint32_t renewal_read_check;
	char *renewal_read_buf;
};

struct host_status {
	uint64_t first_check; /* local monotime */
	uint64_t last_check; /* local monotime */
	uint64_t last_live; /* local monotime */
	uint64_t last_req; /* local monotime */
	uint64_t owner_id;
	uint64_t owner_generation;
	uint64_t timestamp; /* remote monotime */
	uint64_t set_bit_time;
	uint64_t found_dead_ext_gen;
	uint64_t wrote_dead_ext_gen;
	uint16_t io_timeout;
	uint16_t lease_bad;
	uint8_t dead_ext; /* LFL_DEAD_EXT */
	uint8_t no_timeout; /* LFL_NO_TIMEOUT */
	uint8_t warned_fail;
	uint8_t warned_dead;
	uint8_t warned_dead_ext;
	char owner_name[NAME_ID_SIZE] __attribute__ ((nonstring));
};

struct renewal_history {
	uint64_t timestamp;
	int read_ms;
	int write_ms;
	int next_timeouts;
	int next_errors;
};

/* The max number of connections that can get events for a lockspace. */
#define MAX_EVENT_FDS 32

#define SP_EXTERNAL_USED   0x00000001
#define SP_USED_BY_ORPHANS 0x00000002

struct space {
	struct list_head list;
	char space_name[NAME_ID_SIZE] __attribute__ ((nonstring));
	uint32_t version;
	uint32_t space_id; /* used to refer to this space instance in log messages */
	uint64_t host_id;
	uint64_t host_generation;
	struct sync_disk host_id_disk;
	uint32_t disk_major;
	uint32_t disk_minor;
	uint32_t io_timeout;
	uint32_t set_bitmap_seconds;
	uint32_t flags; /* SP_ */
	uint32_t used_retries;
	uint32_t renewal_read_extend_sec; /* defaults to io_timeout */
	uint32_t rindex_op;
	unsigned int set_max_sectors_kb;
	int nodelay;
	int no_timeout;
	int sector_size;
	int align_size;
	int max_hosts;
	int renew_fail;
	int space_dead;
	int killing_pids;
	int external_remove;
	int thread_stop;
	int wd_fd;
	int event_fds[MAX_EVENT_FDS];
	struct sanlk_host_event host_event;
	uint64_t set_event_time;
	pthread_t thread;
	pthread_mutex_t mutex; /* protects lease_status, thread_stop  */
	struct lease_status lease_status;
	struct host_status host_status[DEFAULT_MAX_HOSTS];
	struct renewal_history *renewal_history;
	int renewal_history_size;
	int renewal_history_next;
	int renewal_history_prev;
	int caw; /* compare-and-write flag */
	int caw_dev_use; /* submit caw ioctl to a dev beneath host_id_disk */
	int caw_dev_fd;
	uint32_t caw_dev_major;
	uint32_t caw_dev_minor;
	uint64_t caw_dev_offset;
	char *caw_data; /* 2*sector_size buffer */
};

/* Update lockspace_info() to copy any fields from struct space
   to space_info */

struct space_info {
	uint32_t space_id;
	uint32_t io_timeout;
	uint64_t host_id;
	uint64_t host_generation;
	int sector_size;
	int align_size;
	int killing_pids;
};

#define RX_OP_FORMAT 1
#define RX_OP_CREATE 2
#define RX_OP_DELETE 3
#define RX_OP_LOOKUP 4
#define RX_OP_UPDATE 5
#define RX_OP_REBUILD 6

#define USE_AIO 1
#define NO_AIO 0

#define HOSTID_AIO_CB_SIZE 4
#define WORKER_AIO_CB_SIZE 2
#define DIRECT_AIO_CB_SIZE 1
#define RESOURCE_AIO_CB_SIZE 2
#define LIB_AIO_CB_SIZE 1

struct aicb {
	int used;
	char *buf;
	struct iocb iocb;
};

struct task {
	char name[NAME_ID_SIZE+1];   /* for log messages */

	unsigned int io_count;       /* stats */
	unsigned int to_count;       /* stats */
	unsigned int miscompares;    /* stats */
	unsigned int paxos_areabuf_used; /* stats */
	unsigned int paxos_areabuf_created; /* stats */

	int use_aio;
	int cb_size;
	int paxos_areabuf_size;
	char *paxos_areabuf;         /* paxos_lease_acquire reads of lease area */
	char *iobuf;                 /* delta_lease_renew reads of lease area */
	io_context_t aio_ctx;
	struct aicb *read_iobuf_timeout_aicb;
	struct aicb *callbacks;
};

EXTERN struct task main_task;

struct worker_info {
	unsigned int busy;
	unsigned int cmd_last;
	unsigned int thread_id;
	unsigned int work_count;
	unsigned int io_count;
	unsigned int to_count;
	unsigned int miscompares;
	unsigned int paxos_areabuf_used;
	unsigned int paxos_areabuf_created;
	unsigned int paxos_areabuf_size;
};

/* TODO: change used, suspend, need_free, pid_dead to flags */

#define CL_KILLPATH_PID 0x00000001 /* include pid as killpath arg */
#define CL_RUNPATH_SENT 0x00000002 /* a RUNPATH msg has been sent to helper */

struct client {
	int used;
	int fd;  /* unset is -1 */
	int pid; /* unset is -1 */
	int cmd_active;
	int cmd_last;
	int pid_dead;
	int suspend;
	int need_free;
	int kill_count;
	int tokens_slots;
	uint32_t flags;
	uint32_t restricted;
	uint64_t kill_last;
	char owner_name[SANLK_NAME_LEN+1];
	char killpath[SANLK_HELPER_PATH_LEN];
	char killargs[SANLK_HELPER_ARGS_LEN];
	pthread_mutex_t mutex;
	void *workfn;
	void *deadfn;
	struct token **tokens;
};

/*
 * client array is only touched by main_loop, there is no lock for it.
 * individual cl structs are accessed by worker threads using cl->mutex
 */

EXTERN struct client *client;

/*
 * yes: use caw if supported by device, and api does not disable (opt-out)
 * no: never use caw (ignore api enable)
 * allow: use caw if supported by device, and api enables (opt-in)
 */
#define USE_CAW_YES   1
#define USE_CAW_NO    2
#define USE_CAW_ALLOW 3

#define DEFAULT_WATCHDOG_FIRE_TIMEOUT 60
#define DEFAULT_USE_AIO 1
#define DEFAULT_IO_TIMEOUT 10
#define DEFAULT_GRACE_SEC 40
#define DEFAULT_USE_WATCHDOG 1
#define DEFAULT_HIGH_PRIORITY 0
#define DEFAULT_MLOCK_LEVEL 1 /* 1=CURRENT, 2=CURRENT|FUTURE */
#define DEFAULT_SOCKET_UID 0
#define DEFAULT_SOCKET_GID 0
#define DEFAULT_SOCKET_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
#define DEFAULT_MIN_WORKER_THREADS 2
#define DEFAULT_MAX_WORKER_THREADS 8
#define DEFAULT_SH_RETRIES 8
#define DEFAULT_QUIET_FAIL 1
#define DEFAULT_RENEWAL_HISTORY_SIZE 180 /* about 1 hour with 20 sec renewal interval */
#define DEFAULT_WRITE_INIT_IO_TIMEOUT 60
#define DEFAULT_USE_COMPARE_AND_WRITE USE_CAW_YES

#define DEFAULT_MAX_SECTORS_KB_IGNORE 0     /* don't change it */
#define DEFAULT_MAX_SECTORS_KB_ALIGN  0     /* set it to align size */
#define DEFAULT_MAX_SECTORS_KB_NUM    1024  /* set it to num KB for all lockspaces */

struct spawn_cmd {
	int    argc;
	char **argv; /* argv[argc] is NULL */
};

struct command_line {
	int type;				/* COM_ */
	int action;				/* ACT_ */
	int debug;
	int debug_renew;
	int debug_hosts;
	int debug_clients;
	int debug_io_submit;
	int debug_io_complete;
	int paxos_debug_all;
	uint64_t debug_cmds;
	int max_sectors_kb_ignore;
	int max_sectors_kb_align;
	int max_sectors_kb_num;
	int quiet_fail;
	int wait;
	int use_watchdog;
	int watchdog_fire_timeout;
	int io_timeout;			/* DEFAULT_IO_TIMEOUT or sanlock.conf io_timeout */
	int io_timeout_set;
	int kill_grace_seconds;		/* -g */
	int kill_grace_set;
	int high_priority;		/* -h */
	int get_hosts;			/* -h */
	int names_log_priority;
	int mlock_level;
	int max_worker_threads;
	int aio_arg;
	int write_init_io_timeout;
	int no_timeout;
	int set_bitmap_seconds;
	int persistent;
	int orphan_set;
	int orphan;
	int used_set;
	int used;
	int all;
	int clear_arg;
	int sector_size;
	int align_size;
	int use_hugepages;
	char *uname;			/* -U */
	int uid;				/* -U */
	char *gname;			/* -G */
	int gid;				/* -G */
	int pid;				/* -p */
	int cid;				/* -C */
	char sort_arg;
	uint64_t host_id;			/* -i */
	uint64_t host_generation;		/* -g */
	uint64_t he_event;			/* -e */
	uint64_t he_data;			/* -d */
	uint64_t timestamp;			/* -t */
	int num_hosts;				/* -n */
	int max_hosts;				/* -m */
	int res_count;
	int sh_retries;
	int create_delta_34;
	int create_delta_40;
	int create_paxos_64;
	int use_compare_and_write;
	int compare_and_write_opt1; /* 1 if -C 1 */
	int compare_and_write_opt0; /* 1 if -C 0 */
	int delta_caw_verify;
	int paxos_caw_verify;
	int caw_dpo;
	int caw_fua;
	uint32_t force_mode;
	int renewal_history_size;
	int renewal_read_extend_sec_set; /* 1 if renewal_read_extend_sec is configured */
	uint32_t renewal_read_extend_sec;
	char our_host_name_opt[256]; /* max SANLK_NAME_LEN will be used */
	char *file_path;
	char *dump_path;
	char flag_name[SANLK_NAME_LEN+1];
	int rindex_op;
	struct sanlk_rentry rentry;		/* -e */
	struct sanlk_rindex rindex;		/* -x RINDEX */
	struct sanlk_lockspace lockspace;	/* -s LOCKSPACE */
	struct sanlk_resource *res_args[SANLK_MAX_RESOURCES]; /* -r RESOURCE */
	struct spawn_cmd *spawn_args; /* -c COUNT CMD [ARG...], for ACT_SPAWN */
	int spawn_count;
	int no_disk;
};

EXTERN struct command_line com;

uint32_t cmd_str_to_num(const char *str);
const char *cmd_num_to_str(int cmd);
uint64_t cmd_num_to_debug_flag(uint32_t cmd);
int is_cmd_debug(uint32_t cmd);
void set_cmd_debug(uint32_t cmd);
void clear_cmd_debug(uint32_t cmd);

/* main.c */
const char *host_state_str(uint32_t flags);

/* sanlock_internal.c */
void free_token(struct token *token);
void set_id_bit(int host_id, char *bitmap, char *c);

/* command line types and actions */

#define COM_DAEMON      1
#define COM_CLIENT      2
#define COM_DIRECT      3

enum {
	ACT_STATUS = 1,
	ACT_HOST_STATUS,
	ACT_LOG_DUMP,
	ACT_SHUTDOWN,
	ACT_ADD_LOCKSPACE,
	ACT_INQ_LOCKSPACE,
	ACT_REM_LOCKSPACE,
	ACT_COMMAND, 
	ACT_ACQUIRE, 
	ACT_RELEASE,
	ACT_INQUIRE, 
	ACT_CONVERT, 
	ACT_REQUEST,
	ACT_ACQUIRE_ID,
	ACT_RELEASE_ID,
	ACT_RENEW_ID,
	ACT_DIRECT_INIT,
	ACT_DUMP,
	ACT_NEXT_FREE,
	ACT_READ_LEADER,
	ACT_CLIENT_INIT,
	ACT_CLIENT_READ,
	ACT_CLIENT_ALIGN,
	ACT_EXAMINE,
	ACT_GETS,
	ACT_VERSION,
	ACT_SET_EVENT,
	ACT_SET_CONFIG,
	ACT_WRITE_LEADER,
	ACT_RENEWAL,
	ACT_FORMAT,
	ACT_CREATE,
	ACT_DELETE,
	ACT_LOOKUP,
	ACT_UPDATE,
	ACT_REBUILD,
	ACT_DIRECT_INIT_HOST,
	ACT_CLIENT_INIT_HOST,
	ACT_READ,
	ACT_SET_HOST,
	ACT_SPAWN,
};

EXTERN int external_shutdown;
EXTERN int our_host_name_matches_product_uuid;
EXTERN char our_host_name_global[SANLK_NAME_LEN+1];
EXTERN char our_product_uuid[SANLK_NAME_LEN+1];
EXTERN char our_product_uuid_compact[SANLK_NAME_LEN+1]; /* dash chars omitted from uuid */

EXTERN int daemon_status_num_workers; /* replicate thread pool counter for status reporting */

EXTERN int kill_count_max;
EXTERN int is_helper;
EXTERN int helper_ci;
EXTERN int helper_pid;
EXTERN int helper_kill_fd;
EXTERN int helper_status_fd;
EXTERN uint64_t helper_last_status;
EXTERN uint32_t helper_full_count;
EXTERN int efd;

EXTERN struct list_head spaces;
EXTERN struct list_head spaces_rem;
EXTERN struct list_head spaces_add;
EXTERN pthread_mutex_t spaces_mutex;

/* major.minor.patch-build (TODO: get build) */
EXTERN uint8_t sanlock_version_major;
EXTERN uint8_t sanlock_version_minor;
EXTERN uint8_t sanlock_version_patch;
EXTERN uint8_t sanlock_version_build;
EXTERN uint32_t sanlock_version_combined;

#endif

