#ifndef _CHECKERS_H
#define _CHECKERS_H

#include <pthread.h>
#include "list.h"
#include "defaults.h"

/*
 *
 * Userspace (multipath/multipathd) path states
 *
 * PATH_WILD:
 * - Use: Any checker
 * - Description: Corner case where "fd < 0" for path fd (see checker_check()),
 *   or where a checker detects an unsupported device
 *   (e.g. wrong checker configured for a given device).
 *
 * PATH_UNCHECKED:
 * - Use: Only in directio checker
 * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT
 *   not include in flags, or O_DIRECT read fails
 * - Notes:
 *   - multipathd: uses it to skip over paths in sync_map_state()
 *   - multipath: used in update_paths(); if state==PATH_UNCHECKED, call
 *     pathinfo()
 *
 * PATH_DOWN:
 * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur)
 * - Description: Either a) SG_IO ioctl failed, or b) check condition on some
 *   SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and
 *   you shouldn't try to send commands to it
 *
 * PATH_UP:
 * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur)
 * - Description: Path is up and I/O can be sent to it
 *
 * PATH_SHAKY:
 * - Use: Only emc_clariion
 * - Description: Indicates path not available for "normal" operations
 *
 * PATH_GHOST:
 * - Use: Only hp_sw and rdac
 * - Description: Indicates a "passive/standby" path on active/passive HP
 *   arrays.  These paths will return valid answers to certain SCSI commands
 *   (tur, read_capacity, inquiry, start_stop), but will fail I/O commands.
 *   The path needs an initialization command to be sent to it in order for
 *   I/Os to succeed.
 *
 * PATH_PENDING:
 * - Use: All async checkers
 * - Description: Indicates a check IO is in flight.
 *
 * PATH_TIMEOUT:
 * - Use: Only tur checker
 * - Description: Command timed out
 *
 * PATH REMOVED:
 * - Use: All checkers
 * - Description: Device has been removed from the system
 *
 * PATH_DELAYED:
 * - Use: None of the checkers (returned if the path is being delayed before
 *   reintegration.
 * - Description: If a path fails after being up for less than
 *   delay_watch_checks checks, when it comes back up again, it will not
 *   be marked as up until it has been up for delay_wait_checks checks.
 *   During this time, it is marked as "delayed"
 */
enum path_check_state {
	PATH_WILD = 0,
	PATH_UNCHECKED,
	PATH_DOWN,
	PATH_UP,
	PATH_SHAKY,
	PATH_GHOST,
	PATH_PENDING,
	PATH_TIMEOUT,
	PATH_REMOVED,
	PATH_DELAYED,
	PATH_MAX_STATE
};

#define DIRECTIO     "directio"
#define TUR          "tur"
#define HP_SW        "hp_sw"
#define RDAC         "rdac"
#define EMC_CLARIION "emc_clariion"
#define READSECTOR0  "readsector0"
#define CCISS_TUR    "cciss_tur"
#define NONE         "none"
#define INVALID      "invalid"

#define ASYNC_TIMEOUT_SEC	30

/*
 * strings lengths
 */
#define CHECKER_NAME_LEN 16
#define CHECKER_MSG_LEN 256
#define CHECKER_DEV_LEN 256
#define LIB_CHECKER_NAMELEN 256

/*
 * Generic message IDs for use in checkers.
 */
enum {
	CHECKER_MSGID_NONE = 0,
	CHECKER_MSGID_DISABLED,
	CHECKER_MSGID_NO_FD,
	CHECKER_MSGID_INVALID,
	CHECKER_MSGID_UP,
	CHECKER_MSGID_DOWN,
	CHECKER_MSGID_GHOST,
	CHECKER_MSGID_UNSUPPORTED,
	CHECKER_GENERIC_MSGTABLE_SIZE,
	CHECKER_FIRST_MSGID = 100,	/* lowest msgid for checkers */
	CHECKER_MSGTABLE_SIZE = 100,	/* max msg table size for checkers */
};

struct checker_class;
struct checker {
	struct checker_class *cls;
	int fd;
	unsigned int timeout;
	int disable;
	short msgid;		             /* checker-internal extra status */
	void * context;                      /* store for persistent data */
	void ** mpcontext;                   /* store for persistent data shared
						multipath-wide. */
};

static inline int checker_selected(const struct checker *c)
{
	return c != NULL && c->cls != NULL;
}

const char *checker_state_name(int);
int init_checkers(void);
void cleanup_checkers (void);
int checker_init (struct checker *, void **);
int checker_mp_init(struct checker *, void **);
void checker_clear (struct checker *);
void checker_put (struct checker *);
void checker_reset (struct checker *);
void checker_set_sync (struct checker *);
void checker_set_async (struct checker *);
void checker_set_fd (struct checker *, int);
void checker_enable (struct checker *);
void checker_disable (struct checker *);
/*
 * start_checker_thread(): start async path checker thread
 *
 * This function provides a wrapper around pthread_create().
 * The created thread will call the DSO's "libcheck_thread" function with the
 * checker context as argument.
 *
 * Rationale:
 * Path checkers that do I/O may hang forever. To avoid blocking, some
 * checkers therefore use asynchronous, detached threads for checking
 * the paths. These threads may continue hanging if multipathd is stopped.
 * In this case, we can't unload the checker DSO at exit. In order to
 * avoid race conditions and crashes, the entry point of the thread
 * needs to be in libmultipath, not in the DSO itself.
 *
 * @param arg: pointer to struct checker_context.
 */
struct checker_context {
	struct checker_class *cls;
};
int start_checker_thread (pthread_t *thread, const pthread_attr_t *attr,
			  struct checker_context *ctx);
int checker_check (struct checker *, int);
int checker_is_sync(const struct checker *);
const char *checker_name (const struct checker *);
void reset_checker_classes(void);
/*
 * This returns a string that's best prepended with "$NAME checker",
 * where $NAME is the return value of checker_name().
 */
const char *checker_message(const struct checker *);
void checker_clear_message (struct checker *c);
void checker_get(struct checker *, const char *);

/* Prototypes for symbols exported by path checker dynamic libraries (.so) */
int libcheck_check(struct checker *);
int libcheck_init(struct checker *);
void libcheck_free(struct checker *);
void *libcheck_thread(struct checker_context *ctx);

/*
 * msgid => message map.
 *
 * It only needs to be provided if the checker defines specific
 * message IDs.
 * Message IDs available to checkers start at CHECKER_FIRST_MSG.
 * The msgtable array is 0-based, i.e. msgtable[0] is the message
 * for msgid == __CHECKER_FIRST_MSG.
 * The table ends with a NULL element.
 */
extern const char *libcheck_msgtable[];

#endif /* _CHECKERS_H */
