#ifndef FAILURES_H
#define FAILURES_H

struct ip_addr;

/* Default exit status codes that we could use. */
enum fatal_exit_status {
	FATAL_LOGOPEN	= 80, /* Can't open log file */
	FATAL_LOGWRITE  = 81, /* Can't write to log file */
	FATAL_LOGERROR  = 82, /* Internal logging error */
	FATAL_OUTOFMEM	= 83, /* Out of memory */
	FATAL_EXEC	= 84, /* exec() failed */

	FATAL_DEFAULT	= 89
};

enum log_type {
	LOG_TYPE_DEBUG,
	LOG_TYPE_INFO,
	LOG_TYPE_WARNING,
	LOG_TYPE_ERROR,
	LOG_TYPE_FATAL,
	LOG_TYPE_PANIC,

	LOG_TYPE_COUNT,
	/* special case */
	LOG_TYPE_OPTION
};

struct failure_line {
	pid_t pid;
	enum log_type log_type;
	/* If non-zero, the first log_prefix_len bytes in text indicate
	   the log prefix. This implies disable_log_prefix=TRUE. */
	unsigned int log_prefix_len;
	/* Disable the global log prefix. */
	bool disable_log_prefix;
	const char *text;
};

struct failure_context {
	enum log_type type;
	int exit_status; /* for LOG_TYPE_FATAL */
	const struct tm *timestamp; /* NULL = use time() + localtime() */
	unsigned int timestamp_usecs;
	const char *log_prefix; /* override the default log prefix */
	/* If non-0, insert the log type text (e.g. "Info: ") at this position
	   in the log_prefix instead of appending it. */
	unsigned int log_prefix_type_pos;
};

#define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S "

typedef void failure_callback_t(const struct failure_context *ctx,
				const char *format, va_list args);

extern const char *failure_log_type_prefixes[];
extern const char *failure_log_type_names[];

void i_log_type(const struct failure_context *ctx, const char *format, ...)
	ATTR_FORMAT(2, 3);
void i_log_typev(const struct failure_context *ctx, const char *format,
		 va_list args) ATTR_FORMAT(2, 0);

void i_panic(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD;
void i_unreached(const char *source_filename, int source_linenum)
	ATTR_NORETURN ATTR_COLD;
#define i_unreached() \
	i_unreached(__FILE__, __LINE__)
void i_fatal(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_NORETURN ATTR_COLD;
void i_error(const char *format, ...) ATTR_FORMAT(1, 2) ATTR_COLD;
void i_warning(const char *format, ...) ATTR_FORMAT(1, 2);
void i_info(const char *format, ...) ATTR_FORMAT(1, 2);
void i_debug(const char *format, ...) ATTR_FORMAT(1, 2);

void i_fatal_status(int status, const char *format, ...)
	ATTR_FORMAT(2, 3) ATTR_NORETURN ATTR_COLD;

/* Change failure handlers. */
#ifndef __cplusplus
void i_set_fatal_handler(failure_callback_t *callback ATTR_NORETURN);
#else
/* Older g++ doesn't like attributes in parameters */
void i_set_fatal_handler(failure_callback_t *callback);
#endif
void i_set_error_handler(failure_callback_t *callback);
void i_set_info_handler(failure_callback_t *callback);
void i_set_debug_handler(failure_callback_t *callback);
void i_get_failure_handlers(failure_callback_t **fatal_callback_r,
			    failure_callback_t **error_callback_r,
			    failure_callback_t **info_callback_r,
			    failure_callback_t **debug_callback_r);

/* Send failures to file. */
void default_fatal_handler(const struct failure_context *ctx,
			   const char *format, va_list args)
	ATTR_NORETURN ATTR_FORMAT(2, 0);
void default_error_handler(const struct failure_context *ctx,
			   const char *format, va_list args)
	ATTR_FORMAT(2, 0);

/* Send failures to syslog() */
void i_syslog_fatal_handler(const struct failure_context *ctx,
			    const char *format, va_list args)
	ATTR_NORETURN ATTR_FORMAT(2, 0);
void i_syslog_error_handler(const struct failure_context *ctx,
			    const char *format, va_list args)
	ATTR_FORMAT(2, 0);

/* Open syslog and set failure/info/debug handlers to use it. */
void i_set_failure_syslog(const char *ident, int options, int facility);

/* Send failures to specified log file instead of stderr. */
void i_set_failure_file(const char *path, const char *prefix);

/* Send errors to stderr using internal error protocol. */
void i_set_failure_internal(void);
/* Returns TRUE if the given callback handler was set via
   i_set_failure_internal(). */
bool i_failure_handler_is_internal(failure_callback_t *const callback);
/* If writing to log fails, ignore it instead of existing with
   FATAL_LOGWRITE or FATAL_LOGERROR. */
void i_set_failure_ignore_errors(bool ignore);

/* Send informational messages to specified log file. i_set_failure_*()
   functions modify the info file too, so call this function after them. */
void i_set_info_file(const char *path);

/* Send debug-level message to the given log file. The i_set_info_file()
   function modifies also the debug log file, so call this function after it. */
void i_set_debug_file(const char *path);

/* Set the failure prefix. */
void i_set_failure_prefix(const char *prefix_fmt, ...) ATTR_FORMAT(1, 2);
/* Set prefix to "". */
void i_unset_failure_prefix(void);
/* Returns the current failure prefix (never NULL). */
const char *i_get_failure_prefix(void);
/* Prefix failures with a timestamp. fmt is in strftime() format. */
void i_set_failure_timestamp_format(const char *fmt);
/* When logging with internal error protocol, update the process's current
   IP address / log prefix by sending it to log process. This is mainly used to
   improve the error message if the process crashes. */
void i_set_failure_send_ip(const struct ip_addr *ip);
void i_set_failure_send_prefix(const char *prefix);

/* Call the callback before exit()ing. The callback may update the status. */
void i_set_failure_exit_callback(void (*callback)(int *status));
/* Call the exit callback and exit() */
void failure_exit(int status) ATTR_NORETURN ATTR_COLD;

/* Returns TRUE if any of the logging is configured to /dev/stdout. */
bool i_failure_have_stdout_logs(void);

/* Parse a line logged using internal failure handler */
void i_failure_parse_line(const char *line, struct failure_line *failure);

void failures_deinit(void);

#endif
