[DRBD-cvs] user by phil; LGE's big change to the drbdadm's scanne...

drbd-user@lists.linbit.com drbd-user@lists.linbit.com
Mon, 26 Apr 2004 10:36:34 +0200 (CEST)


DRBD CVS committal

Author  : phil
Project : drbd
Module  : user

Dir     : drbd/user


Modified Files:
      Tag: rel-0_7-branch
	Makefile drbdadm.h drbdadm_adjust.c drbdadm_main.c 
	drbdadm_parser.y drbdadm_scanner.fl drbdsetup.c 


Log Message:
LGE's big change to the drbdadm's scanner and other internals
of the drbdadm. 
* Improves error reporting a lot!
ATTENTION: Changes the syntax of the configuration file! -- This is
the last time we change it before 0.7 

===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Makefile,v
retrieving revision 1.7.2.16
retrieving revision 1.7.2.17
diff -u -3 -r1.7.2.16 -r1.7.2.17
--- Makefile	5 Apr 2004 11:56:00 -0000	1.7.2.16
+++ Makefile	26 Apr 2004 08:36:29 -0000	1.7.2.17
@@ -27,7 +27,9 @@
 	drbdadm_adjust.o
 
 drbdadm_scanner.c: drbdadm_scanner.fl drbdadm_parser.h
-	flex -odrbdadm_scanner.c drbdadm_scanner.fl
+	flex -s -odrbdadm_scanner.c drbdadm_scanner.fl
+
+# for debug:	flex -d -s -odrbdadm_scanner.c drbdadm_scanner.fl
 
 drbdadm_parser.h: drbdadm_parser.y
 	bison -d -o drbdadm_parser.c drbdadm_parser.y
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Attic/drbdadm.h,v
retrieving revision 1.1.2.21
retrieving revision 1.1.2.22
diff -u -3 -r1.1.2.21 -r1.1.2.22
--- drbdadm.h	21 Apr 2004 21:36:53 -0000	1.1.2.21
+++ drbdadm.h	26 Apr 2004 08:36:29 -0000	1.1.2.22
@@ -2,6 +2,20 @@
 #define DRBDADM_H
 
 #include <linux/drbd_config.h>
+#include <sys/utsname.h>
+
+#define E_syntax	  2
+#define E_usage		  3
+#define E_config_invalid 10
+#define E_exec_error     20
+#define E_thinko	 42 /* :) */
+
+/* for check_uniq(): Check for uniqueness of certain values...
+ * comment out if you want to NOT choke on the first conflict */
+#define EXIT_ON_CONFLICT
+
+/* for verify_ips(): make not verifyable ips fatal */
+//#define INVALID_IP_IS_INVALID_CONF
 
 struct d_globals
 {
@@ -34,7 +48,7 @@
   char* protocol;
   char* ind_cmd;
   struct d_host_info* me;
-  struct d_host_info* partner;
+  struct d_host_info* peer;
   struct d_option* net_options;
   struct d_option* disk_options;
   struct d_option* sync_options;
@@ -48,16 +62,22 @@
 extern int adm_syncer(struct d_resource* ,char* );
 extern int m_system(int,char** );
 extern struct d_option* find_opt(struct d_option*,char*);
+extern void validate_resource(struct d_resource *);
+extern int check_uniq(const char* what, const char *fmt, ...);
+extern void verify_ips(struct d_resource* res);
+
 
 extern char* config_file;
 extern int config_valid;
 extern struct d_resource* config;
 extern struct d_globals global_options;
-extern int line;
+extern int line, fline, c_resource_start;
 
 extern int dry_run;
 extern char* drbdsetup;
 extern char ss_buffer[255];
+extern struct utsname nodeinfo;
+
 
 /* ssprintf() places the result of the printf in the current stack
    frame and sets ptr to the resulting string. If the current stack
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Attic/drbdadm_adjust.c,v
retrieving revision 1.1.2.13
retrieving revision 1.1.2.14
diff -u -3 -r1.1.2.13 -r1.1.2.14
--- drbdadm_adjust.c	8 Apr 2004 13:58:54 -0000	1.1.2.13
+++ drbdadm_adjust.c	26 Apr 2004 08:36:29 -0000	1.1.2.14
@@ -21,6 +21,8 @@
    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#define _GNU_SOURCE
+
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -46,13 +48,13 @@
 
   if(pipe(pipes)) {
     perror("Creation of pipes failed");
-    exit(20);
+    exit(E_exec_error);
   }
 
   mpid = fork();
   if(mpid == -1) {
     fprintf(stderr,"Can not fork");
-    exit(20);
+    exit(E_exec_error);
   }
   if(mpid == 0) {
     close(pipes[0]); // close reading end
@@ -60,7 +62,7 @@
     close(pipes[1]);
     execv(argv[0],argv);
     fprintf(stderr,"Can not exec");
-    exit(20);
+    exit(E_exec_error);
   }
 
   close(pipes[1]); // close writing end
@@ -90,7 +92,7 @@
       return r*1024*1024*(1024/def_mult);
     default:
       fprintf(stderr,"%s is not a valid number\n",s);
-      exit(20);
+      exit(E_config_invalid);
     }
 }
 
@@ -264,8 +266,8 @@
   }
 
   rv=m_fscanf(in,"Remote address: %[0-9.]:%s\n",str1,str2);
-  if(rv!=2 || strcmp(str1,res->partner->address) ||
-     strcmp(str2,res->partner->port) ) {
+  if(rv!=2 || strcmp(str1,res->peer->address) ||
+     strcmp(str2,res->peer->port) ) {
     do_connect=1;
   }
 
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Attic/drbdadm_main.c,v
retrieving revision 1.1.2.44
retrieving revision 1.1.2.45
diff -u -3 -r1.1.2.44 -r1.1.2.45
--- drbdadm_main.c	21 Apr 2004 21:36:53 -0000	1.1.2.44
+++ drbdadm_main.c	26 Apr 2004 08:36:29 -0000	1.1.2.45
@@ -25,31 +25,38 @@
 
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
+#include <stdarg.h>
 #include <string.h>
+#include <ctype.h>
 #include <stdlib.h>
+#include <search.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <errno.h>
-#define _GNU_SOURCE
 #include <getopt.h>
 #include <signal.h>
 
 #include "drbdadm.h"
 
-// basic format
-#define INDENT "    "
-#define FMT    INDENT "%-14s"
-#define BFMT   INDENT FMT "\n"
-// assignment format
-#define AFMT0 FMT INDENT " = %s\n"
-#define AFMT  INDENT FMT " = %s\n"
-
+static int indent = 0;
+#define INDENT_WIDTH 4
+#define BFMT  "%s;\n"
+#define IPFMT "%-16s %s:%s;\n"
+#define MDISK "%-16s %s [%s];\n"
+#define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args )
+#define printA(name, val ) \
+	printf("%*s%*s %3s;\n", \
+	  INDENT_WIDTH * indent,"" , \
+	  -24+INDENT_WIDTH * indent, \
+	  name, val )
 
-char* basename;
+char* progname;
 
 struct adm_cmd {
   const char* name;
@@ -78,7 +85,9 @@
 static int sh_md_idx(struct d_resource* ,char* );
 
 char ss_buffer[255];
+struct utsname nodeinfo;
 int line=1;
+int fline, c_resource_start;
 struct d_globals global_options = { 0, 0 };
 char *config_file = NULL;
 struct d_resource* config = NULL;
@@ -127,9 +136,23 @@
 static char* esc(char* str)
 {
   static char buffer[1024];
+  char *ue = str, *e = buffer;
 
-  if(strchr(str,' ')) {
-    snprintf(buffer,1024,"\"%s\"",str);
+  if (!str || !str[0]) {
+	return "\"\"";
+  }
+  if(strchr(str,' ')||strchr(str,'\t')||strchr(str,'\\')) {
+    *e++ = '"';
+    while(*ue) {
+      if (*ue == '"' || *ue == '\\') {
+	  *e++ = '\\';
+      }
+      if (e-buffer >= 1022) { fprintf(stderr,"string too long.\n"); exit(E_syntax); }
+      *e++ = *ue++;
+      if (e-buffer >= 1022) { fprintf(stderr,"string too long.\n"); exit(E_syntax); }
+    }
+    *e++ = '"';
+    *e++ = '\0';
     return buffer;
   }
   return str;
@@ -139,58 +162,60 @@
 {
   if(!opts) return;
 
-  printf(INDENT "%s {\n",name);
+  printI("%s {\n",name); ++indent;
   while(opts) {
-    if(opts->value) printf(AFMT,opts->name,opts->value);
-    else printf(BFMT,opts->name);
+    if(opts->value) printA(opts->name,opts->value);
+    else            printI(BFMT,opts->name);
     opts=opts->next;
   }
-  printf(INDENT "}\n");
+  --indent;
+  printI("}\n");
 }
 
 static void dump_global_info()
 {
   if (global_options.minor_count || global_options.disable_io_hints)
     {
-      printf("global {\n");
+      printI("global {\n"); ++indent;
       if (global_options.disable_io_hints)
-	printf(INDENT "disable_io_hints\n");
+	printI("disable_io_hints;\n");
       if (global_options.minor_count)
-	printf(INDENT "minor_count = %i\n", global_options.minor_count);
-      printf("}\n\n");
+	printI("minor_count = %i;\n", global_options.minor_count);
+      --indent; printI("}\n\n");
     }
 }
 
 static void dump_host_info(struct d_host_info* hi)
 {
   if(!hi) {
-    printf("  # No host section data available.\n");
+    printI("  # No host section data available.\n");
     return;
   }
 
-  printf(INDENT "on %s {\n",esc(hi->name));
-  printf(AFMT, "device"    , esc(hi->device));
-  printf(AFMT, "disk"      , esc(hi->disk));
-  printf(AFMT, "address"   , hi->address);
-  printf(AFMT, "port"      , hi->port);
-  printf(AFMT, "meta-disk" , esc(hi->meta_disk));
-  printf(AFMT, "meta-index", esc(hi->meta_index));
-  printf(INDENT "}\n");
+  printI("on %s {\n",esc(hi->name)); ++indent;
+  printA("device", esc(hi->device));
+  printA("disk"  , esc(hi->disk));
+  printI(IPFMT,"address"   , hi->address, hi->port);
+  if (!strcmp(hi->meta_index,"-1"))
+    printA("meta-disk", "internal");
+  else
+    printI(MDISK,"meta-disk", esc(hi->meta_disk), hi->meta_index);
+  --indent; printI("}\n");
 }
 
 static int adm_dump(struct d_resource* res,char* unused)
 {
-  printf("resource %s {\n",esc(res->name));
-  printf(AFMT0,"protocol",res->protocol);
+  printI("resource %s {\n",esc(res->name)); ++indent;
+  printA("protocol",res->protocol);
   if(res->ind_cmd)
-    printf(AFMT0,"incon-degr-cmd",esc(res->ind_cmd));
+    printA("incon-degr-cmd",esc(res->ind_cmd));
   dump_host_info(res->me);
-  dump_host_info(res->partner);
+  dump_host_info(res->peer);
   dump_options("net",res->net_options);
   dump_options("disk",res->disk_options);
   dump_options("syncer",res->sync_options);
   dump_options("startup",res->startup_options);
-  printf("}\n\n");
+  --indent; printf("}\n\n");
 
   return 0;
 }
@@ -219,7 +244,7 @@
 
   if(strcmp("internal",res->me->meta_disk)==0) r = res->me->disk;
   else r = res->me->meta_disk;
-  
+
   printf("%s\n",r);
 
   return 0;
@@ -275,7 +300,7 @@
     free(f->protocol);
     free(f->ind_cmd);
     free_host_info(f->me);
-    free_host_info(f->partner);
+    free_host_info(f->peer);
     free_options(f->net_options);
     free_options(f->disk_options);
     free_options(f->sync_options);
@@ -299,7 +324,7 @@
   }
 
   fprintf(stderr,"Can not find drbdsetup");
-  exit(20);
+  exit(E_exec_error);
 }
 
 static void alarm_handler(int signo)
@@ -331,12 +356,12 @@
   pid = fork();
   if(pid == -1) {
     fprintf(stderr,"Can not fork");
-    exit(20);
+    exit(E_exec_error);
   }
   if(pid == 0) {
     execv(argv[0],argv);
     fprintf(stderr,"Can not exec");
-    exit(20);
+    exit(E_exec_error);
   }
 
   if( !may_sleep ) {
@@ -350,10 +375,10 @@
       if (errno != EINTR) break;
       if (alarm_raised) {
 	fprintf(stderr,"Child process does not terminate!\nExiting.\n");
-	exit(20);
+	exit(E_exec_error);
       } else {
 	fprintf(stderr,"logic bug in %s:%d\n",__FILE__,__LINE__);
-	exit(20);
+	exit(E_exec_error);
       }
     } else {
       if(WIFEXITED(status)) {
@@ -385,7 +410,7 @@
   while(OPT) { \
     if (argc>=20) {\
       fprintf(stderr,"logic bug in %s:%d\n",__FILE__,__LINE__); \
-      exit(20); \
+      exit(E_thinko); \
     } \
     if(OPT->value) { \
       ssprintf(argv[argc++],"--%s=%s",OPT->name,OPT->value); \
@@ -468,7 +493,7 @@
   argv[argc++]=res->me->device;
   argv[argc++]="net";
   ssprintf(argv[argc++],"%s:%s",res->me->address,res->me->port);
-  ssprintf(argv[argc++],"%s:%s",res->partner->address,res->partner->port);
+  ssprintf(argv[argc++],"%s:%s",res->peer->address,res->peer->port);
   argv[argc++]=res->protocol;
   opt=res->net_options;
   make_options(opt);
@@ -544,7 +569,7 @@
 
   printf("\nUSAGE: %s [OPTION...] [-- DRBDSETUP-OPTION...] COMMAND "
 	 "{all|RESOURCE...}\n\n"
-	 "OPTIONS:\n",basename);
+	 "OPTIONS:\n",progname);
 
   opt=admopt;
   while(opt->name) {
@@ -568,7 +593,98 @@
 
   printf("\nVersion: "REL_VERSION" (api:%d)\n",API_VERSION);
 
-  exit(20);
+  exit(E_usage);
+}
+
+int m_shell_match(const char *subcmd, const char *pattern)
+{
+  int ex, pid;
+  char *cmd = NULL;
+
+#ifdef DEBUG
+  ex = asprintf(&cmd, "[[ `%s` == %s ]]", subcmd, pattern);
+#else
+  ex = asprintf(&cmd, "[[ `%s` == %s ]] &>/dev/null", subcmd, pattern);
+#endif
+  if (ex < 0) { perror("asprintf"); exit(E_thinko); }
+
+  pid = fork();
+  if (pid == -1) { fprintf(stderr, "Can not fork"); exit(E_exec_error); }
+  if (pid == 0) {
+    execl("/bin/bash", progname,
+#ifdef DEBUG
+	"-vxc",
+#else
+	"-c",
+#endif
+	cmd, NULL);
+    fprintf(stderr, "Can not exec");
+    exit(E_exec_error);
+  }
+  waitpid(pid, &ex, 0);
+  free(cmd);
+  return ex;
+}
+
+/* if not verifyable, prints a message to stderr,
+ * and sets config_valid = 0 if INVALID_IP_IS_INVALID_CONF is defined */
+void verify_ips(struct d_resource* res)
+{
+  char *cmd = NULL;
+  char *pat = NULL;
+  char *my_ip = NULL;
+  char *his_ip = NULL;
+  int ex;
+
+  if (!(res && res->me   && res->me->address
+	    && res->peer && res->peer->address)) {
+    fprintf(stderr, "OOPS, no resource info in verify_ips!\n");
+    exit(E_config_invalid);
+  }
+  my_ip  = res->me->address;
+  his_ip = res->peer->address;
+  ex = asprintf(&cmd, "/sbin/ip -o addr show scope global to %s", my_ip);
+  if (ex < 0) { perror("asprintf"); exit(E_thinko); }
+  ex = asprintf(&pat, "*inet\\ %s/*", my_ip);
+  if (ex < 0) { perror("asprintf"); exit(E_thinko); }
+  ex = m_shell_match(cmd, pat);
+  free(cmd); cmd = NULL;
+  free(pat); pat = NULL;
+  if (ex != 0) {
+    ENTRY e, *ep;
+    e.key = e.data = ep = NULL;
+    asprintf(&e.key,"%s:%s",my_ip,res->me->port);
+    ep = hsearch(e, FIND);
+    fprintf(stderr, "%s:%d: in resource %s, on %s:\n\t"
+		    "IP %s not found on this host.\n",
+	    config_file,(int) ep->data,res->name, res->me->name,my_ip);
+#ifdef INVALID_IP_IS_INVALID_CONF
+    config_valid = 0;
+#endif
+    free(e.key);
+    return;
+  }
+  asprintf(&cmd, "/sbin/ip -o route get to %s", his_ip);
+  if (ex < 0) { perror("asprintf"); exit(E_thinko); }
+  asprintf(&pat, "%s\\ dev*src\\ %s\\ \\\\*", his_ip, my_ip);
+  if (ex < 0) { perror("asprintf"); exit(E_thinko); }
+  ex = m_shell_match(cmd, pat);
+  free(cmd); cmd = NULL;
+  free(pat); pat = NULL;
+  if (ex != 0) {
+    ENTRY e, *ep;
+    e.key = e.data = ep = NULL;
+    asprintf(&e.key,"%s:%s",his_ip,res->peer->port);
+    ep = hsearch(e, FIND);
+    fprintf(stderr, "%s:%d: in resource %s:\n\tNo route from me (%s) to peer (%s).\n",
+	    config_file,(int) ep->data,res->name, my_ip, his_ip);
+#ifdef INVALID_IP_IS_INVALID_CONF
+    config_valid = 0;
+#endif
+    return;
+  }
+
+  return;
 }
 
 static char* conf_file[] = {
@@ -577,6 +693,86 @@
     0
 };
 
+/* FIXME
+ * strictly speaking we don't need to check for uniqueness of disk and device names,
+ * but for uniqueness of their major:minor numbers ;-)
+ */
+
+int check_uniq(const char* what, const char *fmt, ...)
+{
+  va_list ap;
+  int rv;
+  ENTRY e, *ep;
+  e.key = e.data = ep = NULL;
+
+  va_start(ap, fmt);
+  rv=vasprintf(&e.key,fmt,ap);
+  va_end(ap);
+
+  if (rv < 0) { perror("vasprintf"); exit(E_thinko); }
+
+#ifdef EXIT_ON_CONFLICT
+  if (!what) {
+    fprintf(stderr,"Oops, unset argument in %s:%d.\n", __FILE__ , __LINE__ );
+    exit(E_thinko);
+  }
+#endif
+  e.data = (void*)fline;
+  ep = hsearch(e, FIND);
+  // fprintf(stderr,"%s: FIND %s: %p\n",res->name,e.key,ep);
+  if (ep) {
+    if (what) {
+      fprintf(stderr,
+	      "%s:%d: conflicting use of %s '%s' ...\n"
+	      "%s:%d: %s '%s' first used here.\n",
+	      config_file, line, what, ep->key,
+	      config_file, (int) ep->data, what, ep->key );
+    }
+    free(e.key);
+    config_valid = 0;
+  } else {
+    ep = hsearch(e, ENTER);
+    // fprintf(stderr,"%s: ENTER %s as %s: %p\n",res->name,e.key,ep->key,ep);
+    if (!ep) {
+      fprintf(stderr, "entry failed.\n");
+      exit(E_thinko);
+    }
+    ep = NULL;
+  }
+#ifdef EXIT_ON_CONFLICT
+  if (ep) exit(E_config_invalid);
+#endif
+  return !ep;
+}
+
+void validate_resource(struct d_resource * res)
+{
+  if (!res->protocol) {
+    fprintf(stderr,
+	    "%s:%d: in resource %s:\n\tprotocol definition missing.\n",
+	    config_file, c_resource_start, res->name);
+    config_valid = 0;
+  } else {
+    res->protocol[0] = toupper(res->protocol[0]);
+  }
+  if (!res->me) {
+    fprintf(stderr,
+	    "%s:%d: in resource %s:\n\tmissing section 'on %s { ... }'.\n",
+	    config_file, c_resource_start, res->name, nodeinfo.nodename);
+    config_valid = 0;
+  }
+  if (!res->peer) {
+    fprintf(stderr,
+	    "%s:%d: in resource %s:\n\t"
+	    "missing section 'on <PEER> { ... }'.\n",
+	    config_file, c_resource_start, res->name);
+    config_valid = 0;
+  }
+  if (res->me && res->peer)
+    verify_ips(res);
+}
+
+
 int main(int argc, char** argv)
 {
   int i,rv;
@@ -586,11 +782,12 @@
   drbdsetup=NULL;
   dry_run=0;
   yyin=NULL;
+  uname(&nodeinfo); /* FIXME maybe fold to lower case ? */
 
-  if( (basename=strrchr(argv[0],'/')) )
-    argv[0] = ++basename;
+  if( (progname=strrchr(argv[0],'/')) )
+    argv[0] = ++progname;
   else
-    basename=argv[0];
+    progname=argv[0];
   if(argc == 1) print_usage(); // arguments missing.
 
   opterr=1;
@@ -614,7 +811,7 @@
 	    yyin=fopen(optarg,"r");
 	    if(!yyin) {
 	      fprintf(stderr,"Can not open '%s'.\n.",optarg);
-	      exit(20);
+	      exit(E_exec_error);
 	    }
 	    ssprintf(config_file,"%s",optarg);
 	  }
@@ -653,7 +850,7 @@
 
   if(cmd==NULL) {
     fprintf(stderr,"Unknown command '%s'.\n",argv[optind]);
-    exit(20);
+    exit(E_usage);
   }
   optind++;
 
@@ -672,28 +869,32 @@
     } while (conf_file[++i]);
   }
   if(!config_file) {
-    exit(20);
+    exit(E_config_invalid);
   }
 
-  yyparse();
+  /*
+   * for check_uniq: check uniqueness of
+   * resource names, ip:port, node:disk and node:device combinations
+   * as well as resource:section ...
+   * hash table to test for uniqness of these values...
+   *  256  (max minors)
+   *  *(
+   *       2 (host sections) * 4 (res ip:port node:disk node:device)
+   *     + 4 (other sections)
+   *     + some more,
+   *       if we want to check for scoped uniqueness of *every* option
+   *   )
+   *     since nobody (?) will actually use more than a dozend minors,
+   *     this should be more than enough.
+   */
+  if (!hcreate(256*((2*4)+4))) {
+    fprintf(stderr,"Insufficient memory.\n");
+    exit(E_exec_error);
+  };
 
-  { // check uniqueness of resource names.
-    struct d_resource *res2,*tmp2;
-    char *name;
-
-    for_each_resource(res,tmp,config) {
-      name = res->name;
-      for_each_resource(res2,tmp2,config) {
-	if( res != res2 && !strcmp(res2->name, name) ) {
-	  fprintf(stderr,"Multiple definitions of resource '%s' found.\n",
-		  name);
-	  exit(10);
-	}
-      }
-    }
-  }
+  yyparse();
 
-  if(!config_valid) exit(10);
+  if(!config_valid) exit(E_config_invalid);
 
   {
     int mc=global_options.minor_count;
@@ -703,7 +904,7 @@
     if( mc && mc<nr_resources ) {
       fprintf(stderr,"You have %d resources but a minor_count of %d in your"
 	      " config!\n",nr_resources,mc);
-      exit(20);
+      exit(E_usage);
     }
   }
 
@@ -721,7 +922,7 @@
         for_each_resource(res,tmp,config) {
 	  if( (rv=cmd->function(res,cmd->arg)) ) {
 	    fprintf(stderr,"drbdsetup exited with code %d\n",rv);
-	    exit(20);
+	    exit(E_exec_error);
 	  }
 	}
       } else {
@@ -731,18 +932,18 @@
 	    if(!strcmp(argv[i],res->name)) goto found;
 	  }
 	  fprintf(stderr,"'%s' not defined in your config.\n",argv[i]);
-	  exit(20);
+	  exit(E_usage);
 	found:
 	  if( (rv=cmd->function(res,cmd->arg)) ) {
 	    fprintf(stderr,"drbdsetup exited with code %d\n",rv);
-	    exit(20);
+	    exit(E_exec_error);
 	  }
 	}
       }
-    } else { // Commands which does not need a resource name
+    } else { // Commands which do not need a resource name
       if( (rv=cmd->function(config,cmd->arg)) ) {
 	fprintf(stderr,"drbdsetup exited with code %d\n",rv);
-	exit(20);
+	exit(E_exec_error);
       }
     }
 
@@ -754,5 +955,5 @@
 void yyerror(char* text)
 {
   fprintf(stderr,"%s:%d: %s\n",config_file,line,text);
-  exit(20);
+  exit(E_syntax);
 }
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Attic/drbdadm_parser.y,v
retrieving revision 1.1.2.21
retrieving revision 1.1.2.22
diff -u -3 -r1.1.2.21 -r1.1.2.22
--- drbdadm_parser.y	5 Apr 2004 11:47:01 -0000	1.1.2.21
+++ drbdadm_parser.y	26 Apr 2004 08:36:29 -0000	1.1.2.22
@@ -1,5 +1,4 @@
 %{
-#include <unistd.h>
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -8,28 +7,31 @@
 #include "drbdadm.h"
 
 extern void yyerror(char* text);
-extern int yylex();
+extern int  yylex(void);
 
-#define APPEND(LIST,ITEM) ({                  \
-  typeof((LIST)) _l = (LIST);                 \
-  typeof((ITEM)) _i = (ITEM);                 \
-  typeof((ITEM)) _t;                          \
-  _i->next = NULL;                            \
-  if (_l == NULL) { _l = _i; }                \
-  else {                                      \
+#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;                                         \
+    _t->next = _i;			      \
+  };					      \
+  _l;					      \
 })
 
 static struct d_resource* c_res;
 static struct d_host_info* c_host;
+static char* c_hostname;
+static int   c_section_start, n_hosts;
 
 static struct d_option* new_opt(char* name,char* value)
 {
   struct d_option* cn = malloc(sizeof(struct d_option));
 
+  /* fprintf(stderr,"%s:%d: %s = %s\n",config_file,line,name,value); */
   cn->name=name;
   cn->value=value;
   cn->mentioned=0;
@@ -40,46 +42,33 @@
 static void derror(char* text)
 {
   config_valid=0;
-  fprintf(stderr,
-	  "%s:%d:\n\t'%s' keyword missing from host section ending here.\n"
-	  "\t(Host sections are those beginning with the 'on' keyword)\n\n",
-	  config_file,line,text);
+  fprintf(stderr, "%s:%d: in resource %s, on %s { ... }:"
+	          " '%s' keyword missing.\n",
+	  config_file,c_section_start,c_res->name,c_hostname,text);
 }
 
-static void derror2(char* text)
+static void host_sec(char *name)
 {
-  config_valid=0;
-  fprintf(stderr,
-	  "%s:%d:\n\t%s\n\tDetected at host section ending here.\n"
-	  "\t(Host sections are those beginning with the 'on' keyword)\n\n",
-	  config_file,line,text);
-}
-
-static void host_sec(char* name)
-{
-  char hostname[255];
-
-  gethostname(hostname,255);
-
-  c_host->name=name;
-  if(c_host->device==0) derror("device");
-  if(c_host->disk==0) derror("disk");
-  if(c_host->address==0) derror("address");
-  if(c_host->port==0) derror("port");
-  if(c_host->meta_disk==0) derror("meta-disk");
-  if(c_host->meta_disk) {
-    if( !strcmp(c_host->meta_disk,"internal") && c_host->meta_index==0) 
-      c_host->meta_index=strdup("-1");
-  }
-  if(c_host->meta_index==0) derror("meta-index");
+  c_host->name = name;
+  if (!c_host->device)	  derror("device");
+  if (!c_host->disk)	  derror("disk");
+  if (!c_host->address)   derror("address");
+  if (!c_host->meta_disk) derror("meta-disk");
 
-  if(strcmp(name,hostname)==0) {
-    if(c_res->me) derror2("Thre are multiple host sections for this host.");
+  if (strcmp(name, nodeinfo.nodename) == 0) {
+    // if (c_res->me) error(...); -- already done by check_uniq in the rules.
     c_res->me = c_host;
   } else {
-    if(c_res->partner) derror2("There are multiple host sections for the peer."
-			       "\n\t(Maybe misspelled local host name?)");
-    c_res->partner = c_host;
+    if (c_res->peer) {
+      config_valid = 0;
+      fprintf(stderr,
+	      "%s:%d: in resource %s, on %s { ... } ... on %s { ... }:\n"
+	      "\tThere are multiple host sections for the peer.\n"
+	      "\tMaybe misspelled local host name '%s'?\n",
+	      config_file, c_section_start, c_res->name,
+	      c_res->peer->name, c_hostname, nodeinfo.nodename);
+    }
+    c_res->peer = c_host;
   }
 }
 
@@ -93,6 +82,30 @@
   return res;
 }
 
+void check_meta_disk()
+{
+  if (strcmp(c_host->meta_disk, "internal")) {
+    if (c_host->meta_index == NULL) {
+      fprintf(stderr, "%s:%d: expected 'meta-disk = %s [index]'.\n",
+	      config_file, fline, c_host->meta_disk);
+    }
+    check_uniq("meta-disk", "%s:%s[%s]", c_hostname,
+	       c_host->meta_disk, c_host->meta_index);
+  } else if (c_host->meta_index) {
+    fprintf(stderr,
+	    "%s:%d: no index allowed with 'meta-disk = internal'.\n",
+	    config_file, fline);
+  } else {
+    c_host->meta_index = strdup("-1");
+  }
+}
+
+#define CHKU(what,val) \
+	c_host->what = val; \
+	check_uniq( #what, "%s:%s",c_hostname,val)
+
+#define CHKS(sname) \
+	check_uniq(sname " section","%s:" sname, c_res->name)
 %}
 
 %union {
@@ -101,117 +114,159 @@
   struct d_resource* d_resource;
 }
 
-%token TK_RESOURCE TK_DISK TK_NET TK_SYNCER TK_ON
-%token TK_PORT TK_DEVICE TK_ADDRESS TK_GLOBAL TK_STARTUP
-%token TK_META_DISK TK_META_INDEX
-%token <txt> TK_PROTOCOL TK_ON_IO_ERROR
-%token <txt> TK_SIZE TK_TIMEOUT TK_CONNECT_INT
-%token <txt> TK_RATE TK_USE_CSUMS TK_SKIP_SYNC TK_PING_INT
-%token <txt> TK_INTEGER TK_STRING TK_IPADDR TK_INCON_DEGR_CMD
-%token <txt> TK_DISABLE_IO_HINTS TK_MINOR_COUNT
+%token TK_GLOBAL TK_RESOURCE
+%token TK_ON TK_NET TK_DISK_S TK_SYNCER TK_STARTUP
+%token TK_MINOR_COUNT TK_DISABLE_IO_HINTS
+%token TK_PROTOCOL TK_INCON_DEGR_CMD
+%token TK_ADDRESS TK_DISK TK_DEVICE TK_META_DISK
+%token <txt> TK_INTEGER TK_STRING
+%token <txt> TK_ON_IO_ERROR TK_SIZE
+%token <txt> TK_TIMEOUT TK_CONNECT_INT TK_PING_INT TK_MAX_BUFFERS TK_IPADDR
+%token <txt> TK_MAX_EPOCH_SIZE TK_SNDBUF_SIZE
+%token <txt> TK_SKIP_SYNC TK_USE_CSUMS TK_RATE TK_SYNC_GROUP TK_AL_EXTENTS
 %token <txt> TK_WFC_TIMEOUT TK_DEGR_WFC_TIMEOUT
-%token <txt> TK_MAX_BUFFERS TK_MAX_EPOCH_SIZE
-%token <txt> TK_SNDBUF_SIZE TK_SYNC_GROUP TK_AL_EXTENTS
-%token <txt> TK_SINTEGER
 
+
+%type <txt> hostname resource_name
 %type <d_option> disk_stmts disk_stmt
 %type <d_option> net_stmts net_stmt
 %type <d_option> sync_stmts sync_stmt
 %type <d_option> startup_stmts startup_stmt
 %type <d_resource> resources resource
-%type <txt> signed_int
 
 %%
-config:           global_sec resources   { config=$2; }
+config:		  global_sec resources	 { config=$2; }
 		;
 
-global_sec:       /* empty */
-                | TK_GLOBAL '{' glob_stmts '}'
+global_sec:	  /* empty */
+		| TK_GLOBAL glob_stmts
 		;
 
-glob_stmts:       /* empty */
+glob_stmts:	  /* empty */
 		| glob_stmts glob_stmt
 		;
 
-glob_stmt:        TK_DISABLE_IO_HINTS   { global_options.disable_io_hints=1; }
-		| TK_MINOR_COUNT '=' TK_INTEGER   { global_options.minor_count=atoi($3); }
-                ;
+glob_stmt:	  TK_DISABLE_IO_HINTS
+			{ global_options.disable_io_hints=1;   }
+		| TK_MINOR_COUNT TK_INTEGER
+			{ global_options.minor_count=atoi($2); }
+		;
+
+resources:	  /* empty */	     { $$ = 0; }
+		| resources resource { $$=APPEND($1,$2); }
+		;
 
-resources:        /* empty */   { $$ = 0; }
-		| resources resource   { $$=APPEND($1,$2); }
+resource:	TK_RESOURCE { n_hosts = 0; } resource_name res_stmts
+			{ $$ = c_res; validate_resource(c_res); }
 		;
 
-resource:	  TK_RESOURCE TK_STRING { c_res = new_resource($2); }
-                  '{' res_stmts '}' { $$ = c_res; }
+resource_name:	TK_STRING
+		{
+			int uniq;
+			c_resource_start = line;
+			c_res		 = new_resource($1);
+			uniq = check_uniq("resource","%s",$1);
+			if (!uniq) exit(E_config_invalid);
+		}
 		;
 
-res_stmts:        /* empty */
+res_stmts:	  /* empty */
 		| res_stmts res_stmt
 		| res_stmts section
 		;
 
-res_stmt:         TK_PROTOCOL '=' TK_STRING   { c_res->protocol=$3; }
-		| TK_INCON_DEGR_CMD '=' TK_STRING   { c_res->ind_cmd=$3; }
+res_stmt:	  TK_PROTOCOL	    TK_STRING { c_res->protocol=$2; }
+		| TK_INCON_DEGR_CMD TK_STRING { c_res->ind_cmd=$2;  }
 		;
 
-section:	  TK_DISK '{' disk_stmts '}' { c_res->disk_options=$3; }
-		| TK_NET  '{' net_stmts '}'  { c_res->net_options=$3; }
-		| TK_ON TK_STRING '{' host_stmts '}' { host_sec($2); }
-		| TK_SYNCER '{' sync_stmts '}' { c_res->sync_options=$3; }
-		| TK_STARTUP '{' startup_stmts '}' {c_res->startup_options=$3;}
+section:	  TK_DISK_S   disk_stmts
+		{ CHKS("disk");    c_res->disk_options=$2;    }
+		| TK_NET      net_stmts
+		{ CHKS("net");     c_res->net_options=$2;     }
+		| TK_SYNCER   sync_stmts
+		{ CHKS("syncer");  c_res->sync_options=$2;    }
+		| TK_STARTUP  startup_stmts
+		{ CHKS("startup"); c_res->startup_options=$2; }
+		| TK_ON hostname host_stmts { host_sec($2); }
 		;
 
-disk_stmts:       /* empty */   { $$ = 0; }
-		| disk_stmts disk_stmt   { $$=APPEND($1,$2); }
+hostname:	TK_STRING
+		{
+		  int uniq;
+		  c_section_start = line;
+		  c_hostname = $1;
+		  uniq = check_uniq("host section", "%s: on %s",
+				    c_res->name, c_hostname);
+		  if (!uniq)
+		    exit(E_config_invalid);
+		  if (++n_hosts > 2) {
+		    fprintf(stderr,
+			    "%s:%d: in resource %s, "
+			    "unsupported third host section on %s { ... }.\n",
+			    config_file, c_section_start, c_res->name,
+			    c_hostname);
+		    exit(E_config_invalid);
+		  }
+		}
 		;
 
-disk_stmt:        TK_ON_IO_ERROR '=' TK_STRING   { $$=new_opt($1,$3); }
-		| TK_SIZE '=' TK_INTEGER   { $$=new_opt($1,$3); }
+disk_stmts:	  /* empty */	           { $$ = 0; }
+		| disk_stmts disk_stmt	   { $$=APPEND($1,$2); }
 		;
 
-net_stmts:        /* empty */   { $$ = 0; }
-		| net_stmts net_stmt   { $$=APPEND($1,$2); }
+disk_stmt:	  TK_ON_IO_ERROR TK_STRING { $$=new_opt($1,$2); }
+		| TK_SIZE TK_INTEGER       { $$=new_opt($1,$2); }
 		;
 
-net_stmt:         TK_TIMEOUT '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_CONNECT_INT '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_PING_INT '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_MAX_BUFFERS '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_MAX_EPOCH_SIZE '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_SNDBUF_SIZE '=' TK_INTEGER   { $$=new_opt($1,$3); }
+net_stmts:	  /* empty */	           { $$ = 0; }
+		| net_stmts net_stmt       { $$=APPEND($1,$2); }
 		;
 
-sync_stmts:       /* empty */   { $$ = 0; }
-		| sync_stmts sync_stmt   { $$=APPEND($1,$2); }
+net_stmt:	  TK_TIMEOUT	    TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_CONNECT_INT    TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_PING_INT	    TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_MAX_BUFFERS    TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_MAX_EPOCH_SIZE TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_SNDBUF_SIZE    TK_INTEGER { $$=new_opt($1,$2); }
 		;
 
-sync_stmt:        TK_SKIP_SYNC   { $$=new_opt($1,0); }
-		| TK_USE_CSUMS   { $$=new_opt($1,0); }
-		| TK_RATE '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_SYNC_GROUP '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_AL_EXTENTS '=' TK_INTEGER   { $$=new_opt($1,$3); }
+sync_stmts:	  /* empty */	           { $$ = 0; }
+		| sync_stmts sync_stmt	   { $$=APPEND($1,$2); }
 		;
 
-host_stmts:       /* empty */  { c_host=calloc(1,sizeof(struct d_host_info)); }
+sync_stmt:	  TK_SKIP_SYNC		   { $$=new_opt($1,0);  }
+		| TK_USE_CSUMS		   { $$=new_opt($1,0);  }
+		| TK_RATE	TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_SYNC_GROUP TK_INTEGER { $$=new_opt($1,$2); }
+		| TK_AL_EXTENTS TK_INTEGER { $$=new_opt($1,$2); }
+		;
+
+host_stmts:	  /* empty */ { c_host=calloc(1,sizeof(struct d_host_info)); }
 		| host_stmts host_stmt
 		;
 
-host_stmt:        TK_DISK '=' TK_STRING     { c_host->disk=$3; }
-		| TK_DEVICE '=' TK_STRING   { c_host->device=$3; }
-		| TK_ADDRESS '=' TK_IPADDR  { c_host->address=$3; }
-		| TK_PORT '=' TK_INTEGER    { c_host->port=$3; }
-		| TK_META_DISK '=' TK_STRING { c_host->meta_disk=$3; }
-		| TK_META_INDEX '=' signed_int { c_host->meta_index=$3; }
+host_stmt:	  TK_DISK    TK_STRING	  { CHKU(disk,$2); }
+		| TK_DEVICE  TK_STRING	  { CHKU(device,$2); }
+		| TK_ADDRESS ip_and_port
+		{ check_uniq("IP","%s:%s", c_host->address,c_host->port); }
+		| TK_META_DISK meta_disk_and_index { check_meta_disk(); }
 		;
 
-signed_int:       TK_SINTEGER
-                | TK_INTEGER
-                ;
 
-startup_stmts:    /* empty */  { $$ = 0; }
+ip_and_port:	  TK_IPADDR TK_INTEGER
+		{ c_host->address=$1; c_host->port = $2; }
+		;
+
+meta_disk_and_index:
+		  TK_STRING TK_INTEGER
+		{ c_host->meta_disk = $1; c_host->meta_index = $2; }
+		| TK_STRING { c_host->meta_disk = $1; }
+		;
+
+startup_stmts:	  /* empty */  { $$ = 0; }
 		| startup_stmts startup_stmt   { $$=APPEND($1,$2); }
 		;
 
-startup_stmt:     TK_WFC_TIMEOUT '=' TK_INTEGER   { $$=new_opt($1,$3); }
-		| TK_DEGR_WFC_TIMEOUT '=' TK_INTEGER   { $$=new_opt($1,$3); }
+startup_stmt:	  TK_WFC_TIMEOUT      TK_INTEGER   { $$=new_opt($1,$2); }
+		| TK_DEGR_WFC_TIMEOUT TK_INTEGER   { $$=new_opt($1,$2); }
 		;
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/Attic/drbdadm_scanner.fl,v
retrieving revision 1.1.2.20
retrieving revision 1.1.2.21
diff -u -3 -r1.1.2.20 -r1.1.2.21
--- drbdadm_scanner.fl	11 Feb 2004 13:25:20 -0000	1.1.2.20
+++ drbdadm_scanner.fl	26 Apr 2004 08:36:29 -0000	1.1.2.21
@@ -1,87 +1,382 @@
 %{
-#include <stdio.h>
-#include <stdlib.h>
+/*
+*/
+
 #include <string.h>
-#include "drbdadm_parser.h"
+#include <ctype.h>
 #include "drbdadm.h"
+#include "drbdadm_parser.h"
 
-void syntax_error(char*);
-
-#if 0
-#define DP printf("'%s' ",yytext)
-#else
-#define DP
-#endif
-
-#define CP yylval.txt = strdup(yytext)
+static void update_lcnt(void);
+static void section(int);
+static void named_section(int);
+static void do_assign(int);
+static void unescape(void);
+static void unescape_midx(void);
+/* static char* const escape(char* const str); */
+static void syntax_error(char*);
+static void expect_error(char*);
+
+#define CP		yylval.txt = strdup(yytext);
+
+#define YY_NO_UNPUT
+static int yy_top_state(void) __attribute((unused)); /* no unused warning */
+
+#define PRINTF(fmt, args...) \
+  fprintf(stderr, "%s:%d: " fmt, config_file, line , ## args )
+
+/* This is an "unusual" scanner. If someone tells me how to do this in
+ * the parser instead, tell me. I just was not able to make the error
+ * handling of yacc do what I want.
+ *		-- lge
+ */ 
+
+/*
+ * Philipp dislikes syntax diversity, and wants terminating semicolons.
+ * So I drop what I thought would be nice to have: plain English and
+ * punctuation...
+ASSIGN			  {LS}*[:=]{LS}*|{LS}(is{LS})?
+DISK			  disk|"on top of"
+IGNORE			  ({WSC}*(use|with)*)+
+NDELIM			  [^ \t\n#=:;{}]+
+*/
 
 %}
 
 %option noyywrap
+%option stack
 
-NUM      [0-9][0-9]*[MKG]?
-SNUMB    [0-9]{1,3}
-IPV4ADDR ({SNUMB}"."){3}{SNUMB}
-WS       [ \t]
-OPCHAR   [{}=]
-%x in_string id
+%x RESOURCE GLOBAL
+%x STARTUP DISK NET SYNCER HOST
+%x SEMICOLON ASSIGN NUM NUM_U NAME STRING PROTO IO_ERROR
+%x IP_AND_PORT PORT META_DISK META_IDX
+%x LS LBRACE IGNORE_SECTION
+
+LS			  [ \t]+
+WS			  [ \t\n]+
+COMMENT			  \#[^\n]*
+WSC			  ({WS}|{COMMENT}\n)+
+ASSIGN			  {LS}
+NUM			  [0-9]+
+NUM_U			  [0-9]+[kmgKMG]? 
+NAME			  [/_.A-Za-z0-9-]+
+STRING			  ({NAME}|\"([^\"\\\n]*|\\.)*\")+
+USTRING			  \"([^\"\\\n]*|\\.)*
+DISK			  disk
+INTERN			  internal|\"internal\"
+META_IDX		  {LS}*\[{LS}*{NUM}{LS}*\]
+O_IDX			  {WSC}meta-index{ASSIGN}
+IGNORE			  {WSC}
+SKIP			  skip({LS}[^{\n]*)?
+NDELIM			  [^ \t\n#;{}]+
+_1_254			  [1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]
+_0_255			  0|[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-5]
+IPV4ADDR		  {_1_254}\.{_0_255}\.{_0_255}\.{_1_254}
+PORT			  {WSC}port{ASSIGN}{NUM}
 
 %%
 
-<in_string>[^\"]*   { DP; CP; return TK_STRING; }
-<in_string>\"       BEGIN(INITIAL);
-
-<id>\"           BEGIN(in_string);
-<id>[a-zA-Z/][a-zA-Z0-9_/.-]* { DP; CP; BEGIN(INITIAL); return TK_STRING; }
-<id>[{}]         { DP; BEGIN(INITIAL); return yytext[0]; }
-<id>=            { DP; return yytext[0];                 }
-<id>{WS}+        /* ignore whitespaces */
-
-\"               BEGIN(in_string);
-\n               { line++; }
-#[^\n]*          /* ignore comments */
-{OPCHAR}         { DP; return yytext[0];                 }
-on               { DP; BEGIN(id); return TK_ON;          }
-net              { DP; return TK_NET;                    }
-disk             { DP; BEGIN(id); return TK_DISK;        }
-port             { DP; return TK_PORT;                   }
-syncer           { DP; return TK_SYNCER;                 }
-device           { DP; BEGIN(id); return TK_DEVICE;      }
-global           { DP; return TK_GLOBAL;                 }
-address          { DP; return TK_ADDRESS;                }
-startup          { DP; return TK_STARTUP;                }
-resource         { DP; BEGIN(id); return TK_RESOURCE;    }
-meta-disk        { DP; BEGIN(id); return TK_META_DISK;   }
-meta-index       { DP; return TK_META_INDEX;             }
-rate             { DP; CP; return TK_RATE;               }
-size             { DP; CP; return TK_SIZE;               }
-group            { DP; CP; return TK_SYNC_GROUP;         }
-timeout          { DP; CP; return TK_TIMEOUT;            }
-ping-int         { DP; CP; return TK_PING_INT;           }
-protocol         { DP; CP; BEGIN(id); return TK_PROTOCOL;}
-on-io-error      { DP; CP; BEGIN(id); return TK_ON_IO_ERROR;}
-use-csums        { DP; CP; return TK_USE_CSUMS;          }
-skip-sync        { DP; CP; return TK_SKIP_SYNC;          }
-al-extents       { DP; CP; return TK_AL_EXTENTS;         }
-minor_count      { DP; CP; return TK_MINOR_COUNT;        }
-connect-int      { DP; CP; return TK_CONNECT_INT;        }
-wfc-timeout      { DP; CP; return TK_WFC_TIMEOUT;        }
-max-buffers      { DP; CP; return TK_MAX_BUFFERS;        }
-incon-degr-cmd   { DP; CP; return TK_INCON_DEGR_CMD;     }
-max-epoch-size   { DP; CP; return TK_MAX_EPOCH_SIZE;     }
-sndbuf-size      { DP; CP; return TK_SNDBUF_SIZE;        }
-degr-wfc-timeout { DP; CP; return TK_DEGR_WFC_TIMEOUT;   }
-disable_io_hints { DP; CP; return TK_DISABLE_IO_HINTS;   }
-{IPV4ADDR}       { DP; CP; return TK_IPADDR;             }
-{NUM}            { DP; CP; return TK_INTEGER;            }
--{NUM}           { DP; CP; return TK_SINTEGER;           }
-{WS}+            /* ignore whitespaces */
-[^ \n\t={}"]+    { syntax_error(yytext); }
+<INITIAL,RESOURCE>{
+  {SKIP}		section(IGNORE_SECTION);
+}
+
+<INITIAL>{
+  {COMMENT}		/* discard last comment if no eol at eof */
+  {WSC}			update_lcnt();
+  global		section(GLOBAL); return TK_GLOBAL;
+  resource		named_section(RESOURCE); return TK_RESOURCE;
+  \}			syntax_error("unmached closing brace.");
+  {NDELIM}		expect_error("one of 'global|resource'");
+}
+
+<RESOURCE,GLOBAL,IGNORE_SECTION,STARTUP,DISK,NET,SYNCER,HOST>{
+  {IGNORE}		update_lcnt();
+  \}			yy_pop_state();
+}
+
+<NAME>{
+  {NAME}		yy_pop_state(); CP; return TK_STRING;
+  [^ \t\n#]+		expect_error("name"); /* not referenced currently */
+}
+
+<SEMICOLON>{
+  {LS}			/* ignore */
+  ;			yy_pop_state();
+  \n                    yy_pop_state(); PRINTF("missing ';'\n"); line++; 
+  {NDELIM}   		expect_error("';'");
+}
+
+<ASSIGN>{
+  {ASSIGN}		yy_pop_state(); fline = line;
+  =			syntax_error("don't use '=' signs anymore!");
+  {WSC}			syntax_error("linebreak between option and value not allowed.\n");
+  {NDELIM}		expect_error("whitespace");
+}
+
+<NUM>{
+  {NUM}			yy_pop_state(); CP; return TK_INTEGER;
+  {NDELIM}		expect_error("integer"); yy_pop_state();
+}
+
+<NUM_U>{
+  {NUM_U}		yy_pop_state(); CP; return TK_INTEGER;
+  {NDELIM}		expect_error("[0-9]+[KMG]?"); yy_pop_state();
+}
+
+<STRING>{
+  {STRING}		yy_pop_state(); unescape(); CP; return TK_STRING;
+  {USTRING}		syntax_error("string terminator missing.\n");
+  {NDELIM}		expect_error("string"); yy_pop_state();
+}
+
+<PROTO>{
+  \"[abcABC]\"		yy_pop_state(); unescape(); CP; return TK_STRING;
+  [abcABC]		yy_pop_state(); CP; return TK_STRING;
+  {NDELIM}		expect_error("one of 'A|B|C'"); yy_pop_state();
+}
+
+<PORT>{
+  :			BEGIN(NUM); /* this is only reached when I have a colon */
+  {WSC}port{ASSIGN}	{
+			  /* or a 'port = ' following the 'address = ' */
+			  update_lcnt(); BEGIN(NUM);
+			  PRINTF("'port=<port>' deprecated, "
+				 "please use 'IP:port' instead.\n");
+			}
+}
+
+<IP_AND_PORT>{
+  {IPV4ADDR}/:{NUM}	BEGIN(PORT); CP; return TK_IPADDR;
+  {IPV4ADDR}/{PORT}	BEGIN(PORT); CP; return TK_IPADDR;
+  {IPV4ADDR}		syntax_error("':PORT' missing from IP:PORT.\n");
+  \"{IPV4ADDR}[^ \t\n]+	syntax_error("please do not quote IP:PORT.\n");
+  [0-9.:]+		expect_error("valid IP");
+  {NDELIM}		expect_error("IP and port 'XXX.XXX.XXX.XXX:PORT'");
+}
+
+<META_IDX>{
+  {META_IDX}		unescape_midx(); yy_pop_state(); CP; return TK_INTEGER;
+  {O_IDX}		{
+			  update_lcnt(); BEGIN(NUM);
+			  PRINTF("'meta-index=<idx>' deprecated, "
+				 "please use 'meta-disk /di/sk [idx]' instead.\n");
+			}
+}
+
+<META_DISK>{
+  {INTERN}		unescape(); yy_pop_state();  CP; return TK_STRING;
+  {INTERN}{META_IDX}	|
+  {INTERN}{O_IDX}	syntax_error("don't give an index for internal meta-data.");
+  {STRING}/{META_IDX}	|
+  {STRING}/{O_IDX}{NUM} unescape(); BEGIN(META_IDX); CP; return TK_STRING;
+  {STRING}{LS}*{NDELIM}	expect_error("meta-disk: index missing; either 'internal' or '/dev/ice/name [index]'");
+  {NDELIM}		expect_error("either 'internal' or '/dev/ice/name [index]'");
+}
+
+<LS>{
+  {LS}			update_lcnt(); yy_pop_state();
+  {NDELIM}		expect_error("whitespace"); yy_pop_state();
+}
+
+<LBRACE>{
+  {WSC}*\{{WSC}*	update_lcnt(); yy_pop_state();
+  {WSC}*[^{ \t\n]+	expect_error("'{'");
+}
+
+<IGNORE_SECTION>{
+  [^{}]+		update_lcnt(); /* no ECHO */
+  \{			yy_push_state(IGNORE_SECTION);
+}
+
+<GLOBAL>{
+  minor_count		do_assign(NUM); CP; return TK_MINOR_COUNT;
+  disable_io_hints	yy_push_state(SEMICOLON); return TK_DISABLE_IO_HINTS;
+  {NDELIM}		expect_error("'minor_count|disable_io_hints'");
+}
+
+<RESOURCE>{
+  on			named_section(HOST); return TK_ON;
+  startup		section(STARTUP);    return TK_STARTUP;
+  syncer		section(SYNCER);     return TK_SYNCER;
+  disk			section(DISK);	     return TK_DISK_S;
+  net			section(NET);	     return TK_NET;
+
+  protocol		do_assign(PROTO);  CP; return TK_PROTOCOL;
+  incon-degr-cmd	do_assign(STRING); CP; return TK_INCON_DEGR_CMD;
+  {NDELIM}		{
+			  expect_error(
+				"one of 'protocol|incon-degr-cmd|"
+				"startup|disk|net|syncer|on <HOSTNAME>'");
+			}
+}
+
+<STARTUP>{
+  wfc-timeout		do_assign(NUM); CP; return TK_WFC_TIMEOUT;
+  degr-wfc-timeout	do_assign(NUM); CP; return TK_DEGR_WFC_TIMEOUT;
+  {NDELIM}		expect_error("one of 'wfc-timeout|degr-wfc-timeout'");
+}
+
+<DISK>{
+  on-io-error		do_assign(IO_ERROR); CP; return TK_ON_IO_ERROR;
+  {NDELIM}		expect_error("'on-io-error'");
+}
+
+<IO_ERROR>{
+  pass_on		|
+  panic			|
+  detach		yy_pop_state(); CP; return TK_STRING;
+  {NDELIM}		expect_error("one of 'pass_on|panic|detach'");
+}
+
+
+<NET>{
+  sndbuf-size		do_assign(NUM_U); CP; return TK_SNDBUF_SIZE;
+  timeout		do_assign(NUM);   CP; return TK_TIMEOUT;
+  ping-int		do_assign(NUM);   CP; return TK_PING_INT;
+  connect-int		do_assign(NUM);   CP; return TK_CONNECT_INT;
+  max-buffers		do_assign(NUM);   CP; return TK_MAX_BUFFERS;
+  max-epoch-size	do_assign(NUM);   CP; return TK_MAX_EPOCH_SIZE;
+  {NDELIM}		expect_error("one of 'sndbuf-size|timeout|ping-int|connect-int|max-buffers|max-epoch-size'");
+}
+
+<SYNCER>{
+  rate			do_assign(NUM_U); CP; return TK_RATE;
+  group			do_assign(NUM);   CP; return TK_SYNC_GROUP;
+  al-extents		do_assign(NUM);   CP; return TK_AL_EXTENTS;
+  {NDELIM}		expect_error("one of 'rate|group|al-extents'");
+}
+
+<HOST>{
+  address		do_assign(IP_AND_PORT); CP; return TK_ADDRESS;
+  device		do_assign(STRING);	CP; return TK_DEVICE;
+  {DISK}		do_assign(STRING);	CP; return TK_DISK;
+  meta-disk		do_assign(META_DISK);	CP; return TK_META_DISK;
+  {NDELIM}		expect_error("one of 'address|device|disk|meta-disk'");
+}
+
+<*>{
+  \{			syntax_error("unexpected opening brace.");
+  \n			syntax_error("unexpected end of line, maybe missing ';' ? ");
+  .			expect_error("something else");
+  <<EOF>>		{
+			  if (YY_START != INITIAL)
+			    syntax_error("unexpected end of file. Maybe missing closing brace?.");
+			  /* else
+			    fprintf(stderr,"\n--- End of File. OK\n");
+			  */
+			  yyterminate();
+			}
+}
 
 %%
 
-void syntax_error(char* text)
+static void syntax_error(char *err)
+{
+  PRINTF("%s\n", err);
+  exit(E_syntax);
+}
+
+static void expect_error(char *err)
+{
+  PRINTF("%s expected, not '%s'.\n", err, yytext);
+  exit(E_syntax);
+}
+
+static void update_lcnt(void)
+{
+  char *p = yytext;
+  while (*p) {
+    if (*p++ == '\n')
+      ++line;
+  }
+}
+
+static void section(int sect)
 {
-  fprintf(stderr,"Unknown token '%s' in line %d.\n",text,line);
-  exit(2);
+  static int s = 0;
+  static int g = 0;
+  if (sect != IGNORE_SECTION) {
+    ++s;
+    if (sect == GLOBAL) {
+      if (s != 1) {
+	if (g)
+	  syntax_error("only one global { ... } section allowed.\n");
+	syntax_error("global { ... } section must be first.\n");
+      }
+      ++g;
+    }
+  }
+  yy_push_state(sect);
+  yy_push_state(LBRACE);
+  fline = line;
+}
+
+static void named_section(int sect)
+{
+  section(sect);
+  yy_push_state(STRING);
+  yy_push_state(LS);
+}
+
+static void do_assign(int what)
+{
+  yy_push_state(SEMICOLON);
+  yy_push_state(what);
+  yy_push_state(ASSIGN);
+}
+
+/*
+static char* const escape(char* const str)
+{
+  static char buffer[1024];
+  char *ue = str, *e = buffer;
+
+  if (!str || !str[0]) {
+	return "\"\"";
+  }
+  *e++ = '"';
+  while(*ue) {
+    if (*ue == '"' || *ue == '\\') {
+	*e++ = '\\';
+    }
+    if (e-buffer >= 1021) { PRINTF("string too long.\n"); exit(E_syntax); }
+    *e++ = *ue++;
+  }
+  *e++ = '"';
+  *e++ = '\0';
+  return buffer;
+}
+*/
+
+static void unescape(void)
+{
+  /* backslash escapes from string */
+  char *ue, *e;
+  e = ue = yytext;
+  for (;;) {
+    if (*ue == '"')
+      ue++;
+    if (*ue == '\\')
+      ue++;
+    if (!*ue)
+      break;
+    *e++ = *ue++;
+  }
+  *e = '\0';
+}
+
+static void unescape_midx(void)
+{
+  char *b;
+  int n;
+
+  b = strrchr(yytext, '[');
+  if (b) {
+    n = atoi(b + 1);
+    n = snprintf(yytext, yyleng, "%d", n);
+    if (0 < n && n < yyleng)
+      return;			/* ok */
+  }
+  fprintf(stderr, "Oops, thinko in %s:%d.\n", __FILE__, __LINE__);
+  exit(E_thinko);
 }
===================================================================
RCS file: /var/lib/cvs/drbd/drbd/user/drbdsetup.c,v
retrieving revision 1.54.2.29
retrieving revision 1.54.2.30
diff -u -3 -r1.54.2.29 -r1.54.2.30
--- drbdsetup.c	21 Apr 2004 21:36:53 -0000	1.54.2.29
+++ drbdsetup.c	26 Apr 2004 08:36:29 -0000	1.54.2.30
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <linux/drbd.h>
+#include <linux/drbd_config.h>
 #define _GNU_SOURCE
 #include <getopt.h>
 #include <stdlib.h>
@@ -50,21 +51,21 @@
 #define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0]))
 
 /* Default values */
-#define DEF_NET_TIMEOUT 60           // 6 seconds
-#define DEF_NET_TRY_CON_I 10         // 10 seconds
-#define DEF_NET_PING_I 10            // 10 seconds
-#define DEF_SYNC_RATE 250
-#define DEF_SYNC_GROUP 0
-#define DEF_WFC_TIMEOUT 0            // forever
-#define DEF_DEGR_WFC_TIMEOUT 60      // 60 Seconds
-#define DEF_SYNC_WFC_TIMEOUT 8       // 8 seconds
-#define DEF_SYNC_DEGR_WFC_TIMEOUT 4  // 4 seconds
-#define DEF_SYNC_AL_EXTENTS 128
-#define DEF_MAX_EPOCH_SIZE 2048      // entries
-#define DEF_MAX_BUFFERS 2048         // entries
-#define DEF_SNDBUF_SIZE (2*65535)    // ~128KB
-#define DEF_DISK_SIZE 0
-#define DEF_ON_IO_ERROR PassOn
+#define DEF_NET_TIMEOUT             60      //  6 seconds
+#define DEF_NET_TRY_CON_I           10      // 10 seconds
+#define DEF_NET_PING_I              10      // 10 seconds
+#define DEF_SYNC_RATE              250
+#define DEF_SYNC_GROUP               0
+#define DEF_WFC_TIMEOUT              0      // forever
+#define DEF_DEGR_WFC_TIMEOUT        60      // 60 Seconds
+#define DEF_SYNC_WFC_TIMEOUT         8      // 8 seconds
+#define DEF_SYNC_DEGR_WFC_TIMEOUT    4      // 4 seconds
+#define DEF_SYNC_AL_EXTENTS        128
+#define DEF_MAX_EPOCH_SIZE        2048      // entries
+#define DEF_MAX_BUFFERS           2048      // entries
+#define DEF_SNDBUF_SIZE           (2*65535) // ~128KB
+#define DEF_DISK_SIZE                0
+#define DEF_ON_IO_ERROR         PassOn
 
 #if 0
 # define ioctl(X...) (fprintf(stderr,"ioctl(%s)\n",#X),0);
@@ -348,7 +349,7 @@
     printf(" %s",eh_names[i]);
     if(i < ARRY_SIZE(eh_names)-1) printf(",");
   }
-    
+
   printf("\n\nVersion: "REL_VERSION" (api:%d)\n",API_VERSION);
   if (addinfo)
       printf("\n%s\n",addinfo);