#ifndef DRBDADM_H
#define DRBDADM_H

#include <linux/drbd_config.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <net/if.h>
#include <stdint.h>
#include <stdarg.h>
#include <fcntl.h>

#ifndef O_CLOEXEC
#warning "O_CLOEXEC undefined, redefining to 0"
#define O_CLOEXEC 0
#endif

#include "config.h"
#include "shared_main.h"

struct d_name
{
  char *name;
  struct d_name *next;
};

struct d_proxy_info
{
  struct d_name *on_hosts;
  char* inside_addr;
  char* inside_port;
  char* inside_af;
  char* outside_addr;
  char* outside_port;
  char* outside_af;
};

struct d_host_info
{
  struct d_name *on_hosts;
  char* device;
  unsigned device_minor;
  char* disk;
  char* address;
  char* port;
  char* meta_disk;
  char* address_family;
  int meta_major;
  int meta_minor;
  char* meta_index;
  struct d_proxy_info *proxy;
  struct d_host_info* next;
  struct d_resource* lower;  /* for device stacking */
  char *lower_name;          /* for device stacking, before bind_stacked_res() */
  int config_line;
  unsigned int by_address:1; /* Match to machines by address, not by names (=on_hosts) */
};

struct d_option
{
  char* name;
  char* value;
  struct d_option* next;
  unsigned int mentioned  :1 ; // for the adjust command.
  unsigned int is_default :1 ; // for the adjust command.
  unsigned int is_escaped :1 ;
};

struct d_resource
{
  char* name;
  char* protocol;

  /* these get propagated to host_info sections later. */
  char* device;
  unsigned device_minor;
  char* disk;
  char* meta_disk;
  char* meta_index;

  struct d_host_info* me;
  struct d_host_info* peer;
  struct d_host_info* all_hosts;
  struct d_option* net_options;
  struct d_option* disk_options;
  struct d_option* sync_options;
  struct d_option* startup_options;
  struct d_option* handlers;
  struct d_option* proxy_options;
  struct d_option* proxy_plugins;
  struct d_resource* next;
  struct d_name *become_primary_on;
  char *config_file; /* The config file this resource is define in.*/
  int start_line;
  unsigned int stacked_timeouts:1;
  unsigned int ignore:1;
  unsigned int stacked:1;        /* Stacked on this node */
  unsigned int stacked_on_one:1; /* Stacked either on me or on peer */
};

extern char *canonify_path(char *path);
extern int adm_attach(struct d_resource* ,const char* );
extern int adm_connect(struct d_resource* ,const char* );
extern int adm_resize(struct d_resource* ,const char* );
extern int adm_syncer(struct d_resource* ,const char* );
extern int adm_generic_s(struct d_resource* ,const char* );
extern int _admm_generic(struct d_resource* ,const char*, int flags);
extern struct d_option* find_opt(struct d_option*,char*);
extern void validate_resource(struct d_resource *);
extern void schedule_dcmd( int (* function)(struct d_resource*,const char* ),
			   struct d_resource* res,
			   char* arg,
			   int order);

extern int version_code_kernel(void);
extern int version_code_userland(void);
extern void warn_on_version_mismatch(void);
extern void uc_node(enum usage_count_type type);
extern int adm_create_md(struct d_resource* res ,const char* cmd);
extern void convert_discard_opt(struct d_resource* res);
extern void convert_after_option(struct d_resource* res);
extern int have_ip(const char *af, const char *ip);

/* See drbdadm_minor_table.c */
extern int register_minor(int minor, const char *path);
extern int unregister_minor(int minor);
extern char *lookup_minor(int minor);

enum pr_flags {
  NoneHAllowed  = 4,
  IgnDiscardMyData = 8
};
extern struct d_resource* parse_resource(char*, enum pr_flags);
extern void post_parse(struct d_resource *config, enum pp_flags);
extern struct d_option *new_opt(char *name, char *value);
extern int name_in_names(char *name, struct d_name *names);
extern char *_names_to_str(char* buffer, struct d_name *names);
extern char *_names_to_str_c(char* buffer, struct d_name *names, char c);
#define NAMES_STR_SIZE 255
#define names_to_str(N) _names_to_str(alloca(NAMES_STR_SIZE+1), N)
#define names_to_str_c(N, C) _names_to_str_c(alloca(NAMES_STR_SIZE+1), N, C)
extern void free_names(struct d_name *names);
extern void set_me_in_resource(struct d_resource* res, int match_on_proxy);
extern void set_peer_in_resource(struct d_resource* res, int peer_required);
extern void set_on_hosts_in_res(struct d_resource *res);
extern void set_disk_in_res(struct d_resource *res);
extern char *proxy_connection_name(struct d_resource *res);
int parse_proxy_settings(struct d_resource *res, int check_proxy_token);
/* conn_name is optional and mostly for compatibility with dcmd */
int do_proxy_conn_up(struct d_resource *res, const char *conn_name);
int do_proxy_conn_down(struct d_resource *res, const char *conn_name);
int do_proxy_conn_plugins(struct d_resource *res, const char *conn_name);

extern char *config_file;
extern char *config_save;
extern int config_valid;
extern struct d_resource* config;
extern struct d_resource* common;
extern int line, fline;
extern struct hsearch_data global_htable;

extern int no_tty;
extern int dry_run;
extern int verbose;
extern char* drbdsetup;
extern char* drbd_proxy_ctl;
extern char ss_buffer[1024];
extern struct utsname nodeinfo;

extern char* setup_opts[10];
extern char* connect_to_host;
extern int soi;


/* ssprintf() places the result of the printf in the current stack
   frame and sets ptr to the resulting string. If the current stack
   frame is destroyed (=function returns), the allocated memory is
   freed automatically */

/*
  // This is the nicer version, that does not need the ss_buffer.
  // But it only works with very new glibcs.

#define ssprintf(...) \
	 ({ int _ss_size = snprintf(0, 0, ##__VA_ARGS__);        \
	 char *_ss_ret = __builtin_alloca(_ss_size+1);           \
	 snprintf(_ss_ret, _ss_size+1, ##__VA_ARGS__);           \
	 _ss_ret; })
*/

#define ssprintf(ptr,...) \
  ptr=strcpy(alloca(snprintf(ss_buffer,sizeof(ss_buffer),##__VA_ARGS__)+1),ss_buffer)

/* CAUTION: arguments may not have side effects! */
#define for_each_resource(res,tmp,config) \
	for (res = (config); res && (tmp = res->next, 1); res = tmp)

#endif

#define APPEND(LIST,ITEM) ({		      \
  typeof((LIST)) _l = (LIST);		      \
  typeof((ITEM)) _i = (ITEM);		      \
  typeof((ITEM)) _t;			      \
  _i->next = NULL;			      \
  if (_l == NULL) { _l = _i; }		      \
  else {				      \
    for (_t = _l; _t->next; _t = _t->next);   \
    _t->next = _i;			      \
  };					      \
  _l;					      \
})


#define PARSER_CHECK_PROXY_KEYWORD (1)
#define PARSER_STOP_IF_INVALID (2)

