[Ocfs2-tools-commits] jlbec commits r490 - in trunk: . clusterbo libo2cb libo2cb/include mount.ocfs2

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Tue Dec 14 17:59:16 CST 2004


Author: jlbec
Date: 2004-12-14 17:59:14 -0600 (Tue, 14 Dec 2004)
New Revision: 490

Added:
   trunk/clusterbo/jconfig.c
   trunk/clusterbo/jconfig.h
   trunk/clusterbo/jiterator.c
   trunk/clusterbo/jiterator.h
   trunk/libo2cb/Makefile
   trunk/libo2cb/include/o2cb.h
   trunk/libo2cb/include/o2cb_abi.h
   trunk/libo2cb/o2cb_abi.c
   trunk/libo2cb/o2cb_err.et.in
Modified:
   trunk/Makefile
   trunk/clusterbo/Makefile
   trunk/configure.in
   trunk/libo2cb/
   trunk/libo2cb/include/
   trunk/libo2cb/include/ocfs2_heartbeat.h
   trunk/libo2cb/include/ocfs2_nodemanager.h
   trunk/libo2cb/include/ocfs2_tcp.h
   trunk/mount.ocfs2/Makefile
Log:

o Land the libo2cb organizational changes



Modified: trunk/Makefile
===================================================================
--- trunk/Makefile	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/Makefile	2004-12-14 23:59:14 UTC (rev 490)
@@ -22,7 +22,7 @@
 $(error could not detect architecture for tools)
 endif
 
-SUBDIRS = libocfs2 fsck.ocfs2 mkfs.ocfs2 mounted.ocfs2 tunefs.ocfs2 debugfs.ocfs2 clusterbo mount.ocfs2 listuuid extras patches
+SUBDIRS = libocfs2 libo2cb fsck.ocfs2 mkfs.ocfs2 mounted.ocfs2 tunefs.ocfs2 debugfs.ocfs2 clusterbo mount.ocfs2 listuuid extras patches
 
 ifdef BUILD_OCFS2CDSL
 SUBDIRS += ocfs2cdsl

Modified: trunk/clusterbo/Makefile
===================================================================
--- trunk/clusterbo/Makefile	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/clusterbo/Makefile	2004-12-14 23:59:14 UTC (rev 490)
@@ -2,16 +2,15 @@
 
 include $(TOPDIR)/Preamble.make
 
-SBIN_PROGRAMS = clusterbo o2cb_ctl
-LIBRARIES = libo2cb.a
+SBIN_PROGRAMS = clusterbo # o2cb_ctl
 
 INCLUDES = -Iinclude -I$(TOPDIR)/libocfs2/include \
-	    -I$(TOPDIR)/libocfs2cluster/include
+	    -I$(TOPDIR)/libo2cb/include
 LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2
 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a
 
-LIBO2CB_LIBS  = -L. -lo2cb
-LIBO2CB_DEPS = libo2cb.a
+LIBO2CB_LIBS  = -L$(TOPDIR)/libo2cb -lo2cb
+LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a
 
 ifdef OCFS_DEBUG
 OPTS += -ggdb
@@ -24,9 +23,6 @@
 
 DEFINES = -DOCFS2_FLAT_INCLUDES -DVERSION=\"$(VERSION)\"
 
-LIBO2CB_CFILES = o2cb_abi.c
-LIBO2CB_OBJS = $(subst .c,.o,$(LIBO2CB_CFILES))
-
 CLUSTERBO_CFILES = clusterbo.c
 CLUSTERBO_OBJS = $(subst .c,.o,$(CLUSTERBO_CFILES))
 
@@ -37,17 +33,11 @@
 
 HEADERS = jconfig.h jiterator.h
 
-CFILES = $(LIBO2CB_CFILES) $(CLUSTERBO_CFILES) $(O2CB_CTL_CFILES)
+CFILES = $(CLUSTERBO_CFILES) $(O2CB_CTL_CFILES)
 
 DIST_FILES = $(CFILES) $(HEADERS)
 DIST_RULES = dist-subdircreate
 
-o2cb_abi_CFLAGS = -fPIC
-libo2cb.a: $(LIBO2CB_OBJS)
-	rm -f $@
-	$(AR) r $@ $^
-	$(RANLIB) $@
-
 clusterbo: $(CLUSTERBO_OBJS) $(LIBOCFS2_DEPS)
 	$(LINK) $(LIBOCFS2_LIBS) $(COM_ERR_LIBS)
 

Added: trunk/clusterbo/jconfig.c
===================================================================
--- trunk/clusterbo/jconfig.c	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/clusterbo/jconfig.c	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,1727 @@
+/*
+ * jconfig.c
+ * 
+ * Routines for handling the config file format of
+ * Helpers::JConfig.
+ *
+ * Copyright (C) 2002 Joel Becker <jlbec at evilplan.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License version 2 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Check G_LOG_DOMAIN before including glib.h */
+#ifndef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "JConfig"
+#endif /* G_LOG_DOMAIN */
+
+#include <glib.h>
+
+#include "jiterator.h"
+#include "jconfig.h"
+
+
+
+/*
+ * Defines
+ */
+#define CF_SCAN_WHITESPACE " \t"
+
+
+
+/*
+ * Types
+ */
+typedef struct _JOutputCtxt JOutputCtxt;
+
+
+/*
+ * Structures
+ */
+struct _JConfigCtxt
+{
+    JConfig *cf;
+    JConfigStanza *cfs;
+    gboolean verbose;
+    gboolean error;
+};
+
+struct _JConfigStanza
+{
+    gchar *stanza_name;
+    GHashTable *attrs;
+};
+
+struct _JConfig
+{
+    gchar *filename;
+    GList *stanza_names;
+    GHashTable *stanzas;
+};
+
+struct _JOutputCtxt
+{
+    FILE *output;
+    gboolean success;
+};
+
+
+
+/*
+ * File globals
+ */
+static const GScannerConfig test_config =
+{
+    ""                  /* cset_skip_characters */,
+    (
+     G_CSET_a_2_z
+     "_0123456789"
+     G_CSET_A_2_Z
+    )                   /* cset_identifier_first */,
+    (
+     G_CSET_a_2_z
+     "_0123456789"
+     G_CSET_A_2_Z
+    )                   /* cset_identifier_nth */,
+    ""                  /* cpair_comment_single */,
+    TRUE                /* case_sensitive */,
+    FALSE               /* skip_comment_multi */,
+    FALSE               /* skip_comment_single */,
+    FALSE               /* scan_comment_multi */,
+    TRUE                /* scan_identifier */,
+    TRUE                /* scan_identifier_1char */,
+    FALSE               /* scan_identifier_NULL */,
+    TRUE                /* scan_symbols */,
+    FALSE               /* scan_binary */,
+    FALSE               /* scan_octal */,
+    FALSE               /* scan_float */,
+    FALSE               /* scan_hex */,
+    FALSE               /* scan_hex_dollar */,
+    FALSE               /* scan_string_sq */,
+    FALSE               /* scan_string_dq */,
+    FALSE               /* numbers_2_int */,
+    FALSE               /* int_2_float */,
+    FALSE               /* identifier_2_string */,
+    FALSE               /* char_2_token */,
+    TRUE                /* symbol_2_token */,
+    FALSE               /* scope_0_fallback */
+};
+
+
+
+
+/*
+ * Forward declarations
+ */
+static JConfig *j_config_parse_any(JConfigCtxt *cfc,
+                                   const gchar *input_name,
+                                   gint input_fd,
+                                   const gchar *input_string,
+                                   gint input_len);
+static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc);
+static void j_config_parse_to_eol(GScanner *scanner);
+static void j_config_parse_comment(GScanner *scanner);
+static void j_config_parse_stanza_name(GScanner *scanner,
+                                       JConfigCtxt *cfc);
+static void j_config_parse_white_start(GScanner *scanner,
+                                       JConfigCtxt *cfc);
+static void j_config_parse_stanza_attr(GScanner *scanner,
+                                       JConfigCtxt *cfc);
+static void j_config_foreach_attr_print(gpointer key,
+                                        gpointer value,
+                                        gpointer o_ctxt);
+static void j_config_foreach_stanza_print(gpointer key,
+                                          gpointer value,
+                                          gpointer o_ctxt);
+static void j_config_attr_names_foreach(gpointer key,
+                                        gpointer value,
+                                        gpointer user_data);
+static void j_config_free_stanza(JConfigStanza *cfs);
+static JConfig *j_config_config_new(void);
+static JConfigStanza *j_config_stanza_new(void);
+static void j_config_free_stanza_node(gpointer key,
+                                      gpointer value,
+                                      gpointer thrway);
+static void j_config_free_config_node(gpointer key,
+                                      gpointer value,
+                                      gpointer thrway);
+static void j_config_free_stanza_proxy(gpointer data, gpointer thrway);
+static void j_config_foreach_attr_append(gpointer key,
+                                         gpointer value,
+                                         gpointer fbuffer);
+static void j_config_foreach_stanza_append(gpointer key,
+                                           gpointer value,
+                                           gpointer fbuffer);
+
+
+
+/*
+ * Functions
+ */
+
+
+/*
+ * JConfigCtxt *j_config_new_context()
+ *
+ * Returns a new JConfigCtxt.
+ */
+JConfigCtxt *j_config_new_context()
+{
+    JConfigCtxt *cfc;
+
+    cfc = g_new(JConfigCtxt, 1);
+    if (cfc == NULL)
+    {
+        g_warning("Unable to create a JConfigCtxt structure: %s\n",
+                  g_strerror(errno));
+        return(NULL);
+    }
+    
+    cfc->cfs = NULL;
+    cfc->cf = NULL;
+    cfc->verbose = TRUE;
+    cfc->error = FALSE;
+
+    return cfc;
+}  /* j_config_context_new() */
+
+
+/*
+ * void j_config_context_free(JConfigCtxt *cfc)
+ * 
+ * Frees a JConfigCtxt.  Does *not* free the associated JConfig.
+ */
+void j_config_context_free(JConfigCtxt *cfc)
+{
+    g_return_if_fail(cfc != NULL);
+    g_free(cfc);
+}  /* j_config_context_free() */
+
+
+/*
+ * gboolean j_config_context_get_error(JConfigCtxt *cfc)
+ *
+ * Set zero for continuing past errors, nonzero to fail.
+ */
+gboolean j_config_context_get_error(JConfigCtxt *cfc)
+{
+    g_return_val_if_fail(cfc != NULL, TRUE);
+
+    return cfc->error;
+}  /* j_config_context_get_error() */
+
+
+/*
+ * void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose)
+ *
+ * Set zero for quiet, nonzero for verbose.
+ */
+void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose)
+{
+    g_return_if_fail(cfc != NULL);
+
+    cfc->verbose = verbose;
+}  /* j_config_context_set_verbose() */
+
+
+/*
+ * static JConfig *j_config_parse_any(JConfigCtxt *cfc,
+ *                                    const gchar *input_name,
+ *                                    gint input_fd,
+ *                                    const gchar *input_string,
+ *                                    gint input_len)
+ *                                 
+ * The real initial parsing routine.
+ * Creates the JConfig and structures and then
+ * calls j_config_parse_base();
+ *                                                                 
+ * some of this is ripped from gtkrc.c
+ */
+static JConfig *j_config_parse_any(JConfigCtxt *cfc,
+                                   const gchar *input_name,
+                                   gint input_fd,
+                                   const gchar *input_string,
+                                   gint input_len)
+{
+    GScanner *scanner;
+    
+    scanner = g_scanner_new((GScannerConfig *)&test_config);
+    
+    if (input_fd >= 0)
+    {
+        g_assert(input_string == NULL);
+        g_scanner_input_file(scanner, input_fd);
+    }
+    else
+    {
+        g_assert(input_string != NULL);
+        g_assert(input_len >= 0);
+        g_scanner_input_text(scanner, input_string, input_len);
+    }
+    scanner->input_name = input_name;
+    
+    cfc->cf = j_config_config_new();
+    if (cfc->cf == NULL)
+    {
+        if (cfc->verbose)
+        {
+            g_warning("Unable to create a JConfig structure: %s\n",
+                      g_strerror(errno));
+        }
+        cfc->error = TRUE;
+        return(NULL);
+    }
+
+    cfc->cf->filename = g_strdup(input_name);
+    j_config_parse_base(scanner, cfc);
+    
+    g_scanner_destroy(scanner);
+    
+    return(cfc->cf);
+}  /* j_config_parse_any() */
+
+
+/*
+ * static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc)
+ * 
+ * The config file is line oriented.  In this scope, it is assumed
+ * that the scanner is situated at the begining of a line _always_.
+ * As such, every sub-function called must return with the scanner
+ * at the beginning of a line (or EOF);
+ * 
+ * This function runs through line by line, deciding what
+ * sub-function should handle each line.
+ */
+static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc)
+{
+    gboolean done;
+    gchar *toknam;
+    GTokenValue *value;
+    
+    value = &scanner->next_value;
+
+    done = FALSE;
+    while (done == FALSE)
+    {
+        GTokenType token;
+
+        token = g_scanner_peek_next_token(scanner);
+        
+        switch (token)
+        {
+            case G_TOKEN_EOF:
+                done = TRUE;
+                break;
+            case G_TOKEN_NONE:
+                /* will this ever happen? */
+                break;
+            case G_TOKEN_ERROR:
+                /* should do something here */
+                break;
+            case G_TOKEN_CHAR:
+                if (strchr(CF_SCAN_WHITESPACE, value->v_char) != NULL)
+                    j_config_parse_white_start(scanner, cfc);
+                else if (value->v_char == '#')
+                    j_config_parse_comment(scanner);
+                else if (value->v_char == '\n')
+                {
+#ifdef DEBUG
+                    g_print("Newline\n");
+#endif /* DEBUG */
+                    g_scanner_get_next_token(scanner);
+                    if (cfc->cfs != NULL)
+                        cfc->cfs = NULL;
+                }
+                else
+                {
+                    if (cfc->verbose)
+                    {
+                        g_warning("Invalid character in stanza name: %c\n",
+                                  value->v_char);
+                    }
+                    cfc->error = TRUE;
+                    j_config_parse_to_eol(scanner);
+                    if (cfc->cfs != NULL)
+                        cfc->cfs = NULL;
+                }
+                break;
+            case G_TOKEN_SYMBOL:
+                /* Another one I don't think I should get */
+                break;
+            case G_TOKEN_IDENTIFIER:
+                toknam = "G_TOKEN_IDENTIFIER";
+                j_config_parse_stanza_name(scanner, cfc);
+                break;
+            default:
+                toknam = "NONE";
+                if (cfc->verbose)
+                    g_warning("Unknown token\n");
+                cfc->error = TRUE;
+                j_config_parse_to_eol(scanner);
+                if (cfc->cfs != NULL)
+                    cfc->cfs = NULL;
+                break;
+        }
+    }
+}  /* j_config_parse_base() */
+
+
+/*
+ * static void j_config_parse_to_eol(GScanner *scanner)
+ * 
+ * This is a simple function that just finds the end of a line,
+ * throwing away anything it sees.
+ */
+static void j_config_parse_to_eol(GScanner *scanner)
+{
+    gboolean done;
+    GTokenType token;
+    GTokenValue *value;
+
+    value = &scanner->value;
+
+    done = FALSE;
+    while (done == FALSE)
+    {
+        token = g_scanner_peek_next_token(scanner);
+        if (token == G_TOKEN_EOF)
+            done = TRUE;
+        else
+        {
+            token = g_scanner_get_next_token(scanner);
+            if ((token = G_TOKEN_CHAR) && (value->v_char == '\n'))
+                done = TRUE;
+        }
+    }
+}  /* j_config_parse_to_eol() */
+
+
+/*
+ * static void j_config_parse_comment(GScanner *scanner)
+ * 
+ * Parses a comment out of the file, throwing it away.
+ */
+static void j_config_parse_comment(GScanner *scanner)
+{
+    GTokenType token;
+    GTokenValue *value;
+    
+    value = &scanner->value;
+    token = g_scanner_get_next_token(scanner);
+    
+    g_assert(token == G_TOKEN_CHAR);
+    g_assert(value->v_char == '#');
+    
+    j_config_parse_to_eol(scanner);
+    
+#ifdef DEBUG
+    g_print("Skipped comment\n");
+#endif /* DEBUG */
+}  /* j_config_parse_comment() */
+
+
+/*
+ * static void j_config_parse_stanza_name(GScanner *scanner,
+ *                                        JConfigCtxt *cfc)
+ * 
+ * If a line starts with a non-whitespace character, it signifies a
+ * new stanza.  Stanza names are defined by the Perl-style regular
+ * expression /^\w+:\s*\n/.  Just by virtue of entering this function,
+ * any current stanza is closed.
+ * 
+ * The gotos are because I decided it was cleaner than a
+ * g_free() at every error.
+ */
+static void j_config_parse_stanza_name(GScanner *scanner,
+                                       JConfigCtxt *cfc)
+{
+    gboolean done;
+    gchar *stanza_name;
+    GTokenType token;
+    GTokenValue *value;
+    
+    value = &scanner->value;
+    token = g_scanner_get_next_token(scanner);
+    
+    g_assert(token == G_TOKEN_IDENTIFIER);
+    
+    if (cfc->cfs != NULL)
+        cfc->cfs = NULL;
+
+    stanza_name = g_strdup(value->v_identifier);
+
+    token = g_scanner_peek_next_token(scanner);
+    if (token == G_TOKEN_EOF)
+    {
+        if (cfc->verbose)
+            g_warning("Invalid stanza name declaration: missing ':'\n");
+        cfc->error = TRUE;
+        goto stanza_name_free;
+    }
+    
+    token = g_scanner_get_next_token(scanner);
+    if (token != G_TOKEN_CHAR)
+    {
+        if (cfc->verbose)
+            g_warning("Invalid stanza name declaration\n");
+        cfc->error = TRUE;
+        j_config_parse_to_eol(scanner);
+        goto stanza_name_free;
+    }
+    if (value->v_char == '\n')
+    {
+        if (cfc->verbose)
+            g_warning("Invalid stanza name declaration: missing ':'\n");
+        cfc->error = TRUE;
+        goto stanza_name_free;
+    }
+    if (value->v_char != ':')
+    {
+        if (cfc->verbose)
+        {
+            g_warning("Invalid character in stanza name declaration: %c\n",
+                      value->v_char);
+        }
+        cfc->error = TRUE;
+        j_config_parse_to_eol(scanner);
+        goto stanza_name_free;
+    }
+    
+    done = FALSE;
+    while (done == FALSE)
+    {
+        token = g_scanner_peek_next_token(scanner);
+        if (token == G_TOKEN_EOF)
+            done = TRUE;
+        else if (token == G_TOKEN_CHAR)
+        {
+            token = g_scanner_get_next_token(scanner);
+            if (value->v_char == '\n')
+                done = TRUE;
+            else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL)
+            {
+                if (cfc->verbose)
+                    g_warning("Trailing garbage on stanza name declaration\n");
+                cfc->error = TRUE;
+                j_config_parse_to_eol(scanner);
+                goto stanza_name_free;
+            }
+        }
+        else
+        {
+            if (cfc->verbose)
+                g_warning("Trailing garbage on stanza name declaration\n");
+            cfc->error = TRUE;
+            j_config_parse_to_eol(scanner);
+            goto stanza_name_free;
+        }
+    }
+    
+    cfc->cfs = j_config_add_stanza(cfc->cf, stanza_name);
+
+#ifdef DEBUG
+    g_print("New stanza: %s\n", stanza_name);
+#endif /* DEBUG */
+
+stanza_name_free:
+    g_free(stanza_name);
+}  /* j_config_parse_stanza_name() */
+
+
+/*
+ * static void j_config_parse_white_start(GScanner *scanner,
+ *                                        JConfigCtxt *cfc)
+ * 
+ * If a line starts with whitespace, it is either a blank line, or an
+ * attribute line.  We skip whitespace lines.
+ * If it looks like an attribute line, we call the proper function.
+ */
+static void j_config_parse_white_start(GScanner *scanner,
+                                       JConfigCtxt *cfc)
+{
+    gboolean done;
+    GTokenType token;
+    GTokenValue *value;
+    
+    value = &scanner->value;
+    token = g_scanner_get_next_token(scanner);
+    
+    g_assert(token == G_TOKEN_CHAR);
+    g_assert(strchr(CF_SCAN_WHITESPACE, value->v_char) != NULL);
+    
+    done = FALSE;
+    while (done == FALSE)
+    {
+        token = g_scanner_peek_next_token(scanner);
+        switch (token)
+        {
+            case G_TOKEN_EOF:
+                done = TRUE;
+                break;
+            case G_TOKEN_IDENTIFIER:
+                j_config_parse_stanza_attr(scanner, cfc);
+                done = TRUE;
+                break;
+            case G_TOKEN_CHAR:
+                token = g_scanner_get_next_token(scanner);
+                if (value->v_char == '\n')
+                {
+#ifdef DEBUG
+                    g_print("Skipped whitespace line\n");
+#endif /* DEBUG */
+                    if (cfc->cfs != NULL)
+                        cfc->cfs = NULL;
+                    done = TRUE;
+                }
+                else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL)
+                {
+                    if (cfc->verbose)
+                    {
+                        g_warning("Invalid character in attribute name: %c\n",
+                                  value->v_char);
+                    }
+                    cfc->error = TRUE;
+                    j_config_parse_to_eol(scanner);
+                    done = TRUE;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+}  /* j_config_parse_white_start() */
+
+
+/*
+ * static void j_config_parse_stanza_attr(GScanner *scanner,
+ *                                        JConfigCtxt *cfc)
+ * 
+ * An attribute is of the form <attribute name>=<attribute value>.
+ * <attribute name> is defined by the Perl-style regular expression
+ * /^\s+\w+\s* /.  Note that the leading whitespace (/^\s+/) has been
+ * stripped by j_config_parse_white_start() already.
+ * <attribute value> is defined as /\s*.*\n/.  The leading whitespace
+ * (between the "=" and the first non-whitespace character in
+ * <attribute value>) is stripped, as is the newline.
+ *
+ * This now supports continuation lines.  An attribute line ending in
+ * '\<newline>' will cause a <newline> to be entered into the buffer
+ * and the next line will be treated as part of the attribute.
+ */
+static void j_config_parse_stanza_attr(GScanner *scanner,
+                                       JConfigCtxt *cfc)
+{
+    gboolean done, multi_line;
+    gchar *attr_name;
+    GString *attr_value;
+    GTokenType token;
+    GTokenValue *value;
+    
+    enum
+    {
+        CF_STATE_ID,
+        CF_STATE_EQUAL,
+        CF_STATE_VALUE
+    } cur_state;
+    
+    value = &scanner->value;
+    token = g_scanner_get_next_token(scanner);
+    
+    g_assert(token == G_TOKEN_IDENTIFIER);
+    
+    if (cfc->cfs == NULL)
+    {
+        if (cfc->verbose)
+            g_warning("Attributes require a matching stanza\n");
+        cfc->error = TRUE;
+        j_config_parse_to_eol(scanner);
+        return;
+    }
+
+    attr_name = g_strdup(value->v_identifier);
+    attr_value = g_string_new(NULL);
+    
+    cur_state = CF_STATE_ID;
+    done = FALSE;
+    multi_line = FALSE;
+    while (done == FALSE)
+    {
+        token = g_scanner_peek_next_token(scanner);
+        if (token == G_TOKEN_EOF)
+        {
+            if (cur_state == CF_STATE_ID)
+            {
+                if (cfc->verbose)
+                    g_warning("Invalid attribute: missing '='\n");
+                cfc->error = TRUE;
+                goto attribute_free;
+            }
+            done = TRUE;
+        }
+        else if (token == G_TOKEN_CHAR)
+        {
+            token = g_scanner_get_next_token(scanner);
+            if (value->v_char == '\n')
+            {
+                if (cur_state == CF_STATE_ID)
+                {
+                    if (cfc->verbose)
+                        g_warning("Invalid attribute: missing '='\n");
+                    cfc->error = TRUE;
+                    goto attribute_free;
+                }
+                if (multi_line == FALSE)
+                    done = TRUE;
+                else
+                {
+                    g_string_append_c(attr_value, value->v_char);
+                    multi_line = FALSE;
+                }
+            }
+            else if (multi_line == TRUE)
+            {
+                g_string_append_c(attr_value, '\\');
+                multi_line = FALSE;
+
+                if (value->v_char == '\\')
+                    multi_line = TRUE;
+                else
+                    g_string_append_c(attr_value, value->v_char);
+            }
+            else if (cur_state == CF_STATE_ID)
+            {
+                if (value->v_char == '=')
+                    cur_state = CF_STATE_EQUAL;
+                else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL)
+                {
+                    if (cfc->verbose)
+                        g_warning("Invalid attribute: expecting '='\n");
+                    cfc->error = TRUE;
+                    j_config_parse_to_eol(scanner);
+                    goto attribute_free;
+                }
+            }
+            else if (cur_state == CF_STATE_EQUAL)
+            {
+                if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL)
+                {
+                    cur_state = CF_STATE_VALUE;
+                    if (value->v_char == '\\')
+                        multi_line = TRUE;
+                    else
+                        g_string_append_c(attr_value, value->v_char);
+                }
+            }
+            else
+            {
+                if (value->v_char == '\\')
+                    multi_line = TRUE;
+                else
+                    g_string_append_c(attr_value, value->v_char);
+            }
+        }
+        else
+        {
+            token = g_scanner_get_next_token(scanner);
+            if (cur_state == CF_STATE_ID)
+            {
+                if (cfc->verbose)
+                    g_warning("Invalid attribute: expecting '='\n");
+                cfc->error = TRUE;
+                j_config_parse_to_eol(scanner);
+                return;
+            }
+            else if (cur_state == CF_STATE_EQUAL)
+                cur_state = CF_STATE_VALUE;
+
+            if (token != G_TOKEN_IDENTIFIER)
+            {
+                if (cfc->verbose)
+                    g_warning("Error parsing value\n");
+                cfc->error = TRUE;
+                j_config_parse_to_eol(scanner);
+                goto attribute_free;
+            }
+            
+            if (multi_line == TRUE)
+            {
+                g_string_append_c(attr_value, '\\');
+                multi_line = FALSE;
+            }
+
+            g_string_append(attr_value, value->v_identifier);
+        }
+    }
+    
+    
+    j_config_set_attribute(cfc->cfs, attr_name, attr_value->str);
+#ifdef DEBUG
+    g_print("Attribute: \"%s\" = \"%s\"\n", attr_name, attr_value->str);
+#endif /* DEBUG */
+    
+attribute_free:
+    g_free(attr_name);
+    g_string_free(attr_value, FALSE);
+}
+
+
+/*
+ * static JConfig *j_config_config_new()
+ * 
+ * Allocates a new JConfig structure
+ */
+static JConfig *j_config_config_new()
+{
+    JConfig *cf;
+    
+    cf = g_new(JConfig, 1);
+    if (cf == NULL) return(NULL);
+    
+    cf->filename = NULL;
+    cf->stanza_names = NULL;
+    cf->stanzas = g_hash_table_new(g_str_hash, g_str_equal);
+    if (cf->stanzas == NULL)
+    {
+        g_free(cf);
+        return(NULL);
+    }
+    
+    return(cf);
+}  /* j_config_config_new() */
+
+
+/*
+ * static JConfigStanza *j_config_stanza_new()
+ * 
+ * Allocates a new JConfigStanza structure
+ */
+static JConfigStanza *j_config_stanza_new()
+{
+    JConfigStanza *cfs;
+    
+    cfs = g_new(JConfigStanza, 1);
+    if (cfs == NULL) return(NULL);
+    
+    cfs->stanza_name = NULL;
+    cfs->attrs = g_hash_table_new(g_str_hash, g_str_equal);
+    if (cfs->attrs == NULL)
+    {
+        g_free(cfs);
+        return(NULL);
+    }
+    
+    return(cfs);
+}  /* j_config_stanza_new() */
+
+
+/*
+ * static void j_config_foreach_attr_print(gpointer key,
+ *                                         gpointer value,
+ *                                         gpointer o_ctxt)
+ *                                   
+ * Prints each attribute -> value pair.
+ */
+static void j_config_foreach_attr_print(gpointer key,
+                                        gpointer value,
+                                        gpointer o_ctxt)
+{
+    gchar delimiter[2] = {'\n', '\0'};
+    gchar **output_lines;
+    gboolean first_line;
+    gint i, rc;
+    JOutputCtxt *ctxt;
+
+    ctxt = (JOutputCtxt *)o_ctxt;
+    if (ctxt->success == FALSE)
+        return;
+
+    if ((value == NULL) || (((gchar *)value)[0] == '\0'))
+    {
+        rc = fprintf(ctxt->output, "\t%s =\n", (gchar *)key);
+        if (rc < 1)
+            ctxt->success = FALSE;
+        return;
+    }
+
+    output_lines = g_strsplit(value, delimiter, 0);
+    if (output_lines == NULL)
+    {
+#if DEBUG
+        g_warning("Unable to allocate memory for multiline attribute: %s\n",
+                  g_strerror(errno));
+#endif
+        ctxt->success = FALSE;
+        return;
+    }
+
+    first_line = TRUE;
+    rc = 0;
+    for (i = 0; output_lines[i] != NULL; i++)
+    {
+        if (first_line == TRUE)
+        {
+            rc = fprintf(ctxt->output, "\t%s = %s",
+                         (gchar *)key, output_lines[i]);
+            first_line = FALSE;
+        }
+        else
+            rc = fprintf(ctxt->output, "\\\n%s", output_lines[i]);
+    }
+    g_strfreev(output_lines);
+
+    if (rc < 1)
+    {
+        ctxt->success = FALSE;
+        return;
+    }
+
+    rc = fprintf(ctxt->output, "\n");
+    if (rc < 1)
+        ctxt->success = FALSE;
+}  /* j_config_foreach_attr_print() */
+
+
+/*
+ * static void j_config_foreach_stanza_print(gpointer key,
+ *                                           gpointer value,
+ *                                           gpointer o_ctxt)
+ *                                     
+ * Runs through each stanza, printing the header and
+ * calling j_config_foreach_attr_print() on the attributes.
+ */
+static void j_config_foreach_stanza_print(gpointer key,
+                                          gpointer value,
+                                          gpointer o_ctxt)
+{
+    gint rc;
+    GList *elem;
+    JConfigStanza *cfs;
+    JOutputCtxt *ctxt;
+    
+    elem = (GList *)value;
+    ctxt = (JOutputCtxt *)o_ctxt;
+    if (ctxt->success == FALSE)
+        return;
+
+    while (elem)
+    {
+        cfs = (JConfigStanza *)elem->data;
+        rc = fprintf(ctxt->output, "%s:\n", (gchar *)key);
+        if (rc < 1)
+        {
+            ctxt->success = FALSE;
+            break;
+        }
+
+        g_hash_table_foreach(cfs->attrs, j_config_foreach_attr_print,
+                             o_ctxt);
+
+        rc = fprintf(ctxt->output, "\n");
+        if (rc < 1)
+        {
+            ctxt->success = FALSE;
+            break;
+        }
+        elem = g_list_next(elem);
+    }
+}  /* j_config_foreach_stanza_print() */
+
+
+/*
+ * gboolean j_config_dump_file(JConfig *cf,
+ *                             const gchar *o_ctxt)
+ * 
+ * Prints the configuration.
+ *
+ * FIXME: This needs to do more work to protect the resulting file from
+ * ENOSPC, etc.
+ */
+gboolean j_config_dump_file(JConfig *cf,
+                            const gchar *output_file)
+{
+    gint rc;
+    JOutputCtxt ctxt;
+
+    g_return_val_if_fail(cf != NULL, FALSE);
+    g_return_val_if_fail(output_file != NULL, FALSE);
+
+    ctxt.success = TRUE;
+    if (strcmp(output_file, "-") == 0)
+        ctxt.output = stdout;
+    else
+        ctxt.output = fopen(output_file, "w");
+
+    if (ctxt.output == NULL)
+    {
+#if DEBUG
+        g_warning("Unable to open file \"%s\" for writing: %s\n",
+                  output_file,
+                  g_strerror(errno));
+#endif
+        return(FALSE);
+    }
+    g_hash_table_foreach(cf->stanzas,
+                         j_config_foreach_stanza_print,
+                         &ctxt);
+    if (strcmp(output_file, "-") != 0)
+    {
+        rc = fclose(ctxt.output);
+        if (rc != 0)
+            ctxt.success = FALSE;
+    }
+
+    return(ctxt.success);
+}  /* j_config_dump_file() */
+
+
+/*
+ * JIterator *j_config_get_stanza_names(JConfig *cf)
+ * 
+ * Returns a list of stanzas contained in this config
+ */
+JIterator *j_config_get_stanza_names(JConfig *cf)
+{
+    g_return_val_if_fail(cf != NULL, NULL);
+
+    return(j_iterator_new_from_list(cf->stanza_names));
+}  /* j_config_get_stanza_names() */
+
+
+/*
+ * JConfigStanza *j_config_get_stanza_nth(JConfig *cf,
+ *                                        const gchar *stanza_name,
+ *                                        guint n)
+ *
+ * Retreives the nth stanza with the given name, or NULL if
+ * that does not exist
+ */
+JConfigStanza *j_config_get_stanza_nth(JConfig *cf,
+                                       const gchar *stanza_name,
+                                       guint n)
+{
+    JConfigStanza *cfs;
+    GList *elem;
+
+    g_return_val_if_fail(cf != NULL, NULL);
+    g_return_val_if_fail(stanza_name != NULL, NULL);
+    
+    elem = g_hash_table_lookup(cf->stanzas, stanza_name);
+    if (elem == NULL)
+        return(NULL);
+    
+    elem = g_list_nth(elem, n);
+    cfs = elem != NULL ? (JConfigStanza *)elem->data : NULL;
+
+    return(cfs);
+}  /* j_config_get_stanza_nth() */
+
+
+/*
+ * gchar *j_config_get_stanza_name(JConfigStanza *cfs)
+ *
+ * Returns the stanza's name
+ */
+gchar *j_config_get_stanza_name(JConfigStanza *cfs)
+{
+    g_return_val_if_fail(cfs != NULL, NULL);
+
+    return(g_strdup(cfs->stanza_name));
+}  /* j_config_get_stanza_name() */
+
+
+/*
+ * JConfigStanza *j_config_add_stanza(JConfig *cf,
+ *                                    const gchar *stanza_name);
+ *
+ * Adds a new stanza to the configuration structure
+ */
+JConfigStanza *j_config_add_stanza(JConfig *cf,
+                                   const gchar *stanza_name)
+{
+    JConfigStanza *cfs;
+    GList *elem;
+
+    g_return_val_if_fail(cf != NULL, NULL);
+    g_return_val_if_fail(stanza_name != NULL, NULL);
+
+    cfs = j_config_stanza_new();
+    if (cfs == NULL)
+    {
+#if DEBUG
+        g_warning("Unable to allocate memory for a new stanza: %s\n",
+                  g_strerror(errno));
+#endif
+        return NULL;
+    }
+
+    cfs->stanza_name = g_strdup(stanza_name);
+    elem = (GList *)g_hash_table_lookup(cf->stanzas, stanza_name);
+    if (elem == NULL)
+    {
+        cf->stanza_names =
+            g_list_append(cf->stanza_names, g_strdup(stanza_name));
+        elem = g_list_append(NULL, cfs);
+        g_hash_table_insert(cf->stanzas,
+                            g_strdup(stanza_name),
+                            elem);
+    }
+    else
+        g_list_append(elem, cfs);
+
+    return(cfs);
+}  /* j_config_add_stanza() */
+
+
+/*
+ * void j_config_delete_stanza(JConfig *cf,
+ *                             JConfigStanza *cfs)
+ *
+ * Removes the stanza pointed to by cfs from the configuration.
+ */
+void j_config_delete_stanza(JConfig *cf,
+                            JConfigStanza *cfs)
+{
+    GList *elem, *new_elem;
+    gpointer orig_key;
+    gchar *orig_data;
+
+    g_return_if_fail(cf != NULL);
+    g_return_if_fail(cfs != NULL);
+
+    elem = (GList *)g_hash_table_lookup(cf->stanzas, cfs->stanza_name);
+    if (elem == NULL)
+    {
+#if DEBUG
+        g_warning("Unable to remove stanza - no such class: %s\n",
+                  cfs->stanza_name);
+#endif
+        return;
+    }
+
+    new_elem = g_list_remove(elem, cfs);
+    if (elem == NULL)
+    {
+        if (g_hash_table_lookup_extended(cf->stanzas,
+                                         cfs->stanza_name,
+                                         &orig_key,
+                                         NULL))
+        {
+            g_hash_table_remove(cf->stanzas, cfs->stanza_name);
+            g_free(orig_key);
+        }
+
+        elem = cf->stanza_names;
+        while (elem != NULL)
+        {
+            orig_data = (gchar *)elem->data;
+            if (strcmp(cfs->stanza_name, orig_data) == 0)
+            {
+                cf->stanza_names =
+                    g_list_remove(cf->stanza_names, elem->data);
+                g_free(orig_data);
+                break;
+            }
+            elem = g_list_next(elem);
+        }
+    }
+    else if (elem != new_elem)
+    {
+        if (g_hash_table_lookup_extended(cf->stanzas,
+                                         cfs->stanza_name,
+                                         &orig_key,
+                                         NULL))
+        {
+            g_hash_table_remove(cf->stanzas, cfs->stanza_name);
+            g_free(orig_key);
+        }
+        g_hash_table_insert(cf->stanzas,
+                            g_strdup(cfs->stanza_name),
+                            new_elem);
+    }
+
+    j_config_free_stanza(cfs);
+}  /* j_config_delete_stanza() */
+
+
+/*
+ * void j_config_delete_stanza_nth(JConfig *cf,
+ *                                 const gchar *stanza_name,
+ *                                 guint n)
+ *
+ * Removes the nth stanza of class stanza_name from the
+ * configuration.
+ */
+void j_config_delete_stanza_nth(JConfig *cf,
+                                const gchar *stanza_name,
+                                guint n)
+{
+    JConfigStanza *cfs;
+
+    g_return_if_fail(cf != NULL);
+    g_return_if_fail(stanza_name != NULL);
+
+    cfs = j_config_get_stanza_nth(cf, stanza_name, n);
+    if (cfs == NULL)
+        return;
+
+    j_config_delete_stanza(cf, cfs);
+}  /* j_config_delete_stanza() */
+
+
+/*
+ * JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc,
+ *                                           const gchar *filename)
+ * 
+ * Parses a config file
+ */
+JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc,
+                                          const gchar *filename)
+{
+    gint fd;
+    JConfig *cf;
+
+    g_return_val_if_fail(cfc != NULL, NULL);
+    g_return_val_if_fail(filename != NULL, NULL);
+
+    if (strcmp(filename, "-") == 0)
+        fd = STDIN_FILENO;
+    else
+        fd = open(filename, O_RDONLY);
+
+    if (fd < 0)
+    {
+        if (cfc->verbose)
+        {
+            g_warning("Unable to open file \"%s\": %s\n",
+                      filename,
+                      g_strerror(errno));
+        }
+        cfc->error = TRUE;
+        return(NULL);
+    }
+    
+    cf = j_config_parse_any(cfc, filename, fd, NULL, 0);
+    
+    if (strcmp(filename, "-") != 0)
+        close(fd);
+
+    return(cf);
+}  /* j_config_parse_file_with_context() */
+
+
+/*
+ * JConfig *j_config_parse_file(const gchar *filename)
+ * 
+ * Parses a config file, creating the context.
+ */
+JConfig *j_config_parse_file(const gchar *filename)
+{
+    JConfigCtxt *cfc;
+    JConfig *cf = NULL;
+
+    cfc = j_config_new_context();
+    if (cfc)
+    {
+        cf = j_config_parse_file_with_context(cfc, filename);
+        j_config_context_free(cfc);
+    }
+
+    return(cf);
+}  /* j_config_parse_file() */
+
+
+/*
+ * JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc,
+ *                                             gchar *buffer,
+ *                                             gint buf_len)
+ * 
+ * Parses a config from a text buffer
+ */
+JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc,
+                                            gchar *buffer,
+                                            gint buf_len)
+{
+    JConfig *cf;
+    
+    g_return_val_if_fail(cfc != NULL, NULL);
+    g_return_val_if_fail(buffer != NULL, NULL);
+
+    if (buf_len < 0)
+        buf_len = 0;
+    
+    cf = j_config_parse_any(cfc, "memory", -1, buffer, buf_len);
+    
+    return(cf);
+}  /* j_config_parse_memory() */
+
+
+/*
+ * JConfig *j_config_parse_memory(gchar *buffer, gint buf_len)
+ * 
+ * Parses a buffer, creating the context.
+ */
+JConfig *j_config_parse_memory(gchar *buffer, gint buf_len)
+{
+    JConfigCtxt *cfc;
+    JConfig *cf = NULL;
+
+    cfc = j_config_new_context();
+    if (cfc)
+    {
+        cf = j_config_parse_memory_with_context(cfc, buffer, buf_len);
+        j_config_context_free(cfc);
+    }
+
+    return(cf);
+}  /* j_config_parse_file() */
+
+
+/*
+ * JConfigMatch *j_config_match_build(guint num_matches, ...)
+ *
+ * Convenience function to build an array of JConfigMatch structures.
+ * num_matches is the number of key, value pairs passed to this
+ * function.  The number of varargs passed should therefore be double
+ * num_matches.  eg:
+ *
+ * j_config_match_build(2, "foo", "bar", "quuz", "quuuz");
+ *
+ * This function builds a match that requires:
+ *
+ *     foo = bar
+ *     quuz = quuuz
+ *
+ * Strings passed in are *NOT* copied.  They are merely referenced.
+ * Because of this, the returned array of JConfigMatch structures can
+ * be freed with g_free().  The caller must take care not to change or
+ * destroy the referened strings until they are done with the
+ * array of JConfigMatch structures.
+ */
+JConfigMatch *j_config_match_build(guint num_matches, ...)
+{
+    guint i;
+    va_list args;
+    JConfigMatch *matches;
+
+    matches = g_new0(JConfigMatch, num_matches);
+
+    va_start(args, num_matches);
+    for (i = 0; i < num_matches; i++)
+    {
+        matches[i].type = J_CONFIG_MATCH_VALUE;
+        matches[i].name = va_arg(args, gchar *);
+        if (matches[i].name == NULL)
+        {
+            g_free(matches);
+            matches = NULL;
+            break;
+        }
+        matches[i].value = va_arg(args, gchar *);
+    }
+    va_end(args);
+
+    return(matches);
+}  /* j_config_match_build() */
+
+
+/*
+ * JIterator *j_config_get_stanzas(JConfig *cf,
+ *                                 const gchar *stanza_name,
+ *                                 JConfigMatch *matches,
+ *                                 guint num_matches)
+ * 
+ * Gets the list of stanzas with a given name.  Optionally
+ * return only those that staitisfy matches.  matches is an array
+ * of JConfigMatch structures.  num_matches describes the number
+ * of items in the array.  This array can be built by the caller
+ * explicitly, or the caller can use the convenience function
+ * j_config_match_build().  If num_matches is 0, matches can be
+ * NULL.
+ */
+JIterator *j_config_get_stanzas(JConfig *cf,
+                                const gchar *stanza_name,
+                                JConfigMatch *matches,
+                                guint num_matches)
+{
+    gint i;
+    JConfigStanza *cfs;
+    gchar *value;
+    GList *elem, *tmp;
+    JIterator *iter;
+    
+    g_return_val_if_fail(cf != NULL, NULL);
+    g_return_val_if_fail(stanza_name != NULL, NULL);
+    g_return_val_if_fail((num_matches == 0) || (matches != NULL), NULL);
+
+    elem = (GList *)g_hash_table_lookup(cf->stanzas, stanza_name);
+
+    if (num_matches > 0)
+    {
+        tmp = NULL;
+        for (; elem != NULL; elem = g_list_next(elem))
+        {
+            cfs = (JConfigStanza *)elem->data;
+            g_assert(cfs != NULL);
+
+            for (i = 0; i < num_matches; i++)
+            {
+                g_assert(matches[i].name != NULL);
+                value = j_config_get_attribute(cfs, matches[i].name);
+                if (value != NULL)
+                {
+                    if (matches[i].value == NULL)
+                    {
+                        if (value[0] != '\0')
+                        {
+                            g_free(value);
+                            break;
+                        }
+                    }
+                    else if (strcmp(value, matches[i].value) != 0)
+                    {
+                        g_free(value);
+                        break;
+                    }
+                    g_free(value);
+                }
+                else
+                {
+                    if (matches[i].value != '\0')
+                        break;
+                }
+            }
+            if (i >= num_matches) /* All tests succeeded */
+                tmp = g_list_prepend(tmp, cfs);
+        }
+        elem = g_list_reverse(tmp);
+
+        iter = j_iterator_new_from_list(elem);
+        g_list_free(elem);
+    }
+    else
+        iter = j_iterator_new_from_list(elem);
+    
+    return(iter);
+}  /* j_config_get_stanzas() */
+
+
+/*
+ * void j_config_set_attribute(JConfigStanza *cfs,
+ *                             const gchar *attr_name,
+ *                             const gchar *attr_value)
+ *
+ * Sets the value of a given attribute name
+ */
+void j_config_set_attribute(JConfigStanza *cfs,
+                            const gchar *attr_name,
+                            const gchar *attr_value)
+{
+    g_return_if_fail(cfs != NULL);
+    g_return_if_fail(attr_name != NULL);
+
+    j_config_delete_attribute(cfs, attr_name);
+    g_hash_table_insert(cfs->attrs,
+                        g_strdup(attr_name),
+                        g_strdup(attr_value));
+}  /* j_config_set_attribute() */
+
+
+/*
+ * static void j_config_attr_names_foreach(gpointer key,
+ *                                         gpointer value,
+ *                                         gpointer user_data)
+ *
+ * Foreach function to build the attribute names list.
+ */
+static void j_config_attr_names_foreach(gpointer key,
+                                        gpointer value,
+                                        gpointer user_data)
+{
+    GList **list = (GList **)user_data;
+
+    *list = g_list_append(*list, key);
+}  /* j_config_attr_names_foreach() */
+
+
+/*
+ * JIterator *j_config_get_attribute_names(JConfigStanza *cfs)
+ *
+ * Returns an iterator over every attribute name in the stanza.
+ */
+JIterator *j_config_get_attribute_names(JConfigStanza *cfs)
+{
+    JIterator *iter;
+    GList *attr_names = NULL;
+
+    g_return_val_if_fail(cfs != NULL, NULL);
+
+    g_hash_table_foreach(cfs->attrs, j_config_attr_names_foreach,
+                         &attr_names);
+
+    iter = j_iterator_new_from_list(attr_names);
+    g_list_free(attr_names);
+
+    return(iter);
+}  /* j_config_get_attribute_names() */
+
+
+/*
+ * gchar *j_config_get_attribute(JConfigStanza *cfs,
+ *                               const gchar *attr_name)
+ * 
+ * Retrieves the value of a given attribute name
+ */
+gchar *j_config_get_attribute(JConfigStanza *cfs,
+                              const gchar *attr_name)
+{
+    gchar *s;
+
+    g_return_val_if_fail(cfs != NULL, NULL);
+    g_return_val_if_fail(attr_name != NULL, NULL);
+    
+    s = (gchar *)g_hash_table_lookup(cfs->attrs, attr_name);
+    s = g_strdup(s);
+
+    return(s);
+}  /* j_config_get_attribute() */
+
+
+/*
+ * void j_config_delete_attribute(JConfigStanza *cfs,
+ *                                const gchar *attr_name)
+ *
+ * Removes the attribute attr_name from the stanza pointed to
+ * by cfs.
+ */
+void j_config_delete_attribute(JConfigStanza *cfs,
+                               const gchar *attr_name)
+{
+    gpointer orig_key, orig_value;
+
+    g_return_if_fail(cfs != NULL);
+    g_return_if_fail(attr_name != NULL);
+
+    if (g_hash_table_lookup_extended(cfs->attrs,
+                                     attr_name,
+                                     &orig_key,
+                                     &orig_value))
+    {
+        g_hash_table_remove(cfs->attrs,
+                            attr_name);
+        g_free(orig_key);
+        g_free(orig_value);
+    }
+}  /* j_config_delete_attribute() */
+
+
+/*
+ * static void j_config_free_stanza_node(gpointer key,
+ *                                       gpointer value,
+ *                                       gpointer thrway)
+ * 
+ * Removes a node from a stanza
+ */
+static void j_config_free_stanza_node(gpointer key,
+                                      gpointer value,
+                                      gpointer thrway)
+{
+    g_free(key);
+    g_free(value);
+}  /* j_config_free_stanza_node() */
+
+
+/*
+ * static void j_config_free_stanza(JConfigStanza *cfs)
+ * 
+ * Clears a config file stanza
+ */
+static void j_config_free_stanza(JConfigStanza *cfs)
+{
+    g_return_if_fail(cfs != NULL);
+
+    g_hash_table_foreach(cfs->attrs, j_config_free_stanza_node, NULL);
+    g_hash_table_destroy(cfs->attrs);
+    g_free(cfs->stanza_name);
+    g_free(cfs);
+}  /* j_config_free_stanza() */
+
+
+/*
+ * static void j_config_free_stanza_proxy(gpointer data,
+ *                                        gpointer thrway)
+ * 
+ * A proxy to call j_config_free_stanza() as a GFunc()
+ */
+static void j_config_free_stanza_proxy(gpointer elem_data,
+                                       gpointer thrway)
+{
+    j_config_free_stanza(elem_data);
+}  /* j_config_free_stanza_proxy() */
+
+
+/*
+ * static void j_config_free_config_node(gpointer key,
+ *                                       gpointer value,
+ *                                       gpointer thrway)
+ * 
+ * Removes a node from a config
+ */
+static void j_config_free_config_node(gpointer key,
+                                      gpointer value,
+                                      gpointer thrway)
+{
+    GList *elem;
+    
+    elem = (GList *)value;
+    g_list_foreach(elem, j_config_free_stanza_proxy, NULL);
+    g_list_free(elem);
+    g_free(key);
+}  /* j_config_free_config_node() */
+
+
+/*
+ * void j_config_free(JConfig *cf)
+ * 
+ * Deletes a config file tree
+ */
+void j_config_free(JConfig *cf)
+{
+    g_return_if_fail(cf != NULL);
+
+    g_hash_table_foreach(cf->stanzas, j_config_free_config_node, NULL);
+    g_hash_table_destroy(cf->stanzas);
+    g_free(cf->filename);
+    g_free(cf);
+}  /* j_config_free() */
+
+
+/*
+ * static void j_config_foreach_attr_append(gpointer key,
+ *                                          gpointer value,
+ *                                          gpointer fbuffer)
+ *                                   
+ * Prints each attribute -> value pair.
+ */
+static void j_config_foreach_attr_append(gpointer key,
+                                         gpointer value,
+                                         gpointer fbuffer)
+{
+    gchar delimiter[2] = {'\n', '\0'};
+    gchar **output_lines;
+    gboolean first_line;
+    gint i;
+    GString *buffer;
+
+    g_return_if_fail(fbuffer != NULL);
+
+    buffer = (GString *)fbuffer;
+
+    if ((value == NULL) || (((gchar *)value)[0] == '\0'))
+    {
+        g_string_append_printf(buffer, "\t%s =\n", (gchar *)key);
+        return;
+    }
+
+    output_lines = g_strsplit(value, delimiter, 0);
+    if (output_lines == NULL)
+    {
+        g_warning("Unable to allocate memory for multiline attribute: %s\n",
+                  g_strerror(errno));
+        return;
+    }
+
+    first_line = TRUE;
+    for (i = 0; output_lines[i] != NULL; i++)
+    {
+        if (first_line == TRUE)
+        {
+            g_string_append_printf(buffer, "\t%s = %s", 
+                              (gchar *)key, output_lines[i]);
+            first_line = FALSE;
+        }
+        else
+            g_string_append_printf(buffer, "\\\n%s", output_lines[i]);
+    }
+    g_string_append_printf(buffer, "\n");
+
+    g_strfreev(output_lines);
+}  /* j_config_foreach_attr_append() */
+
+
+/*
+ * static void j_config_foreach_stanza_append(gpointer key,
+ *                                            gpointer value,
+ *                                            gpointer fbuffer)
+ *                                     
+ * Runs through each stanza, printing the header and
+ * calling j_config_foreach_attr_append() on the attributes.
+ */
+static void j_config_foreach_stanza_append(gpointer key,
+                                           gpointer value,
+                                           gpointer fbuffer)
+{
+    GList *elem;
+    JConfigStanza *cfs;
+    GString *buffer;
+    
+    g_return_if_fail(fbuffer != NULL);
+
+    buffer = (GString *)fbuffer;
+    elem = (GList *)value;
+
+    while (elem)
+    {
+        cfs = (JConfigStanza *)elem->data;
+        g_string_append_printf(buffer, "%s:\n", (gchar *)key);
+        g_hash_table_foreach(cfs->attrs, j_config_foreach_attr_append,
+                             fbuffer);
+        g_string_append(buffer, "\n");
+        elem = g_list_next(elem);
+    }
+}  /* j_config_foreach_stanza_append() */
+
+
+/*
+ * gchar *j_config_dump_memory(JConfig *cf)
+ * 
+ * Prints the configuration to an in-memory string.
+ */
+gchar *j_config_dump_memory(JConfig *cf)
+{
+    gchar *output_text;
+    GString *output;
+
+    g_return_val_if_fail(cf != NULL, FALSE);
+
+    output = g_string_new(NULL);
+    if (output == NULL)
+        return(NULL);
+    g_hash_table_foreach(cf->stanzas,
+                         j_config_foreach_stanza_append,
+                         (gpointer)output);
+
+    output_text = output->str;
+    g_string_free(output, FALSE);
+    return(output_text);
+}  /* j_config_dump_memory() */
+

Added: trunk/clusterbo/jconfig.h
===================================================================
--- trunk/clusterbo/jconfig.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/clusterbo/jconfig.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,103 @@
+/*
+ * jconfig.h
+ * 
+ * Header file for configuration file parser
+ *
+ * Copyright (C) 2002 Joel Becker <jlbec at evilplan.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License version 2 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __JCONFIG_H
+#define __JCONFIG_H
+
+
+
+/*
+ * Type definitions
+ */
+typedef struct _JConfigStanza JConfigStanza;
+typedef struct _JConfig JConfig;
+typedef struct _JConfigMatch JConfigMatch;
+typedef struct _JConfigCtxt JConfigCtxt;
+
+
+
+/*
+ * Enums
+ */
+typedef enum _JConfigMatchType
+{
+    J_CONFIG_MATCH_VALUE = 0,
+} JConfigMatchType;
+
+
+
+/*
+ * Structures
+ */
+struct _JConfigMatch
+{
+    JConfigMatchType type;
+    gchar *name;
+    gchar *value;
+};
+
+
+
+/*
+ * Functions
+ */
+
+JConfig *j_config_parse_file(const gchar *filename);
+JConfig *j_config_parse_memory(gchar *buffer, gint buf_len);
+JConfigCtxt *j_config_new_context(void);
+void j_config_context_free(JConfigCtxt *cfc);
+void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose);
+gboolean j_config_context_get_error(JConfigCtxt *cfc);
+JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc,
+                                          const gchar *filename);
+JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc,
+                                            gchar *buffer,
+                                            gint buf_len);
+JIterator *j_config_get_stanza_names(JConfig *cf);
+JConfigMatch *j_config_match_build(guint num_matches, ...);
+JIterator *j_config_get_stanzas(JConfig *cf,
+                                const gchar *stanza_name,
+                                JConfigMatch *matches,
+                                guint num_matches);
+JConfigStanza *j_config_get_stanza_nth(JConfig *cf,
+                                       const gchar *stanza_name,
+                                       guint n);
+gchar *j_config_get_stanza_name(JConfigStanza *cfs);
+JConfigStanza *j_config_add_stanza(JConfig *cf,
+                                   const gchar *stanza_name);
+void j_config_delete_stanza(JConfig *cf, JConfigStanza *cfs);
+void j_config_delete_stanza_nth(JConfig *cf,
+                                const gchar *stanza_name,
+                                guint n);
+JIterator *j_config_get_attribute_names(JConfigStanza *cfs);
+gchar *j_config_get_attribute(JConfigStanza *cfs,
+                              const gchar *attr_name);
+void j_config_set_attribute(JConfigStanza *cfs,
+                            const gchar *attr_name,
+                            const gchar *attr_value);
+void j_config_delete_attribute(JConfigStanza *cfs,
+                               const gchar *attr_name);
+gboolean j_config_dump_file(JConfig *cf, const gchar *output_file);
+gchar *j_config_dump_memory(JConfig *cf);
+void j_config_free(JConfig *cf);
+
+#endif /* __JCONFIG_H */

Added: trunk/clusterbo/jiterator.c
===================================================================
--- trunk/clusterbo/jiterator.c	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/clusterbo/jiterator.c	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,179 @@
+/* 
+ * jiterator.c
+ *
+ * Code for opaque iterators.
+ *
+ * Copyright (C) 2001 Oracle Corporation, Joel Becker
+ * <joel.becker at oracle.com>
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have recieved a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+ 
+/*
+ * MT safe
+ */
+
+#include <sys/types.h>
+#include <glib.h>
+
+#include "jiterator.h"
+
+
+/* Structures
+ */
+struct _JIterator
+{
+  gpointer context;
+  JIteratorFunc      has_more_func;
+  JIteratorFunc      get_next_func;
+  GDestroyNotify        notify_func;
+};
+
+
+static gpointer j_iterator_list_has_more       (gpointer   context);
+static gpointer j_iterator_list_get_next       (gpointer   context);
+static void     j_iterator_list_destroy_notify (gpointer   context);
+
+
+
+/* Enumerations
+ */
+JIterator*
+j_iterator_new (gpointer               context,
+            JIteratorFunc       has_more_func,
+            JIteratorFunc       get_next_func,
+            GDestroyNotify         notify_func)
+{
+  JIterator *iterator;
+
+  iterator = g_new (JIterator, 1);
+  iterator->context = context;
+  iterator->has_more_func = has_more_func;
+  iterator->get_next_func = get_next_func;
+  iterator->notify_func = notify_func;
+
+  return iterator;
+}  /* j_iterator_new() */
+
+JIterator*
+j_iterator_new_from_list (GList *init_list)
+{
+  JIterator *iterator;
+  GList *list_copy, *header;
+
+  /* The list is copied here, the caller is responsible for the
+   * data items.  On _free(), the list is removed and the data items
+   * left alone.  If the caller wants different semantics, the
+   * caller can specify their own functions with _new() */
+  list_copy = g_list_copy(init_list);
+
+  /* This is a header element to refer to the list */
+  header = g_list_prepend(NULL, (gpointer) list_copy);
+
+  iterator = j_iterator_new ((gpointer) header,
+                             j_iterator_list_has_more,
+                             j_iterator_list_get_next,
+                             j_iterator_list_destroy_notify);
+
+  return(iterator);
+}  /* j_iterator_new_from_list() */
+
+gboolean
+j_iterator_has_more (JIterator *iterator)
+{
+  gpointer result;
+
+  g_return_val_if_fail (iterator != NULL, FALSE);
+
+  result = (*iterator->has_more_func) (iterator->context);
+
+  return (gboolean) result;
+}  /* j_iterator_has_more() */
+
+gpointer
+j_iterator_get_next (JIterator *iterator)
+{
+  gpointer result;
+
+  g_return_val_if_fail (iterator != NULL, NULL);
+
+  result = (*iterator->get_next_func) (iterator->context);
+
+  return result;
+}  /* j_iterator_get_next() */
+
+void
+j_iterator_free (JIterator *iterator)
+{
+  (*iterator->notify_func) (iterator->context);
+
+  g_free(iterator);
+}  /* j_iterator_free() */
+
+static gpointer
+j_iterator_list_has_more (gpointer context)
+{
+  GList *header, *elem;
+  gboolean result;
+
+  g_return_val_if_fail (context != NULL, (gpointer) FALSE);
+
+  header = (GList *) context;
+  elem = (GList *) header->data;
+
+  result = (elem != NULL);
+
+  return (gpointer) result;
+}  /* j_iterator_list_has_more() */
+
+static gpointer
+j_iterator_list_get_next (gpointer context)
+{
+  GList *header, *elem;
+  gpointer result;
+
+  g_return_val_if_fail(context != NULL, NULL);
+
+  header = (GList *) context;
+  elem = (GList *) header->data;
+
+  /* User should have called has_more() */
+  g_return_val_if_fail(elem != NULL, NULL);
+
+  result = elem->data;
+
+  header->data = (gpointer) g_list_next(elem);
+
+  g_list_free_1(elem);
+
+  return result;
+}  /* j_iterator_list_get_next() */
+
+static void
+j_iterator_list_destroy_notify (gpointer context)
+{
+  GList *header, *elem;
+
+  g_return_if_fail (context != NULL);
+
+  header = (GList *) context;
+  elem = (GList *) header->data;
+
+  g_list_free(elem);  /* NULL if at end of list */
+
+  g_list_free(header);
+}  /* j_iterator_list_destroy_notify() */

Added: trunk/clusterbo/jiterator.h
===================================================================
--- trunk/clusterbo/jiterator.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/clusterbo/jiterator.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,42 @@
+/*
+ * jiterator.h
+ *
+ * Prototypes for JIterator
+ *
+ * Copyright (C) 2002 Joel Becker <jlbec at evilplan.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License version 2 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JITERATOR_H
+#define __JITERATOR_H
+
+
+/* Typedefs */
+typedef struct _JIterator		JIterator;
+typedef gpointer (*JIteratorFunc)	(gpointer context);
+
+
+/* Functions */
+JIterator* j_iterator_new(gpointer context,
+                          JIteratorFunc has_more_func,
+                          JIteratorFunc get_next_func,
+                          GDestroyNotify notify_func);
+JIterator* j_iterator_new_from_list(GList *init_list);
+gboolean j_iterator_has_more(JIterator *iterator);
+gpointer j_iterator_get_next(JIterator *iterator);
+void j_iterator_free(JIterator *iterator);
+
+#endif /* __JITERATOR_H */
+

Modified: trunk/configure.in
===================================================================
--- trunk/configure.in	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/configure.in	2004-12-14 23:59:14 UTC (rev 490)
@@ -192,6 +192,7 @@
 AC_OUTPUT([
 Config.make
 libocfs2/ocfs2_err.et
+libo2cb/o2cb_err.et
 debugfs.ocfs2/debugfs.ocfs2.8
 mkfs.ocfs2/mkfs.ocfs2.8
 fsck.ocfs2/fsck.ocfs2.8


Property changes on: trunk/libo2cb
___________________________________________________________________
Name: svn:ignore
   + .*.sw?
*.d
debug_*
o2cb_err.c
o2cb_err.h
o2cb_err.et
cscope*
libo2cb.a


Added: trunk/libo2cb/Makefile
===================================================================
--- trunk/libo2cb/Makefile	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/Makefile	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,83 @@
+TOPDIR = ..
+
+include $(TOPDIR)/Preamble.make
+
+WARNINGS = -Wall -Wstrict-prototypes -Wmissing-prototypes \
+	-Wmissing-declarations
+
+ifdef OCFS_DEBUG
+OPTS += -ggdb
+else
+OPTS += -O2
+endif
+
+INCLUDES = -Iinclude
+
+LIBRARIES = libo2cb.a
+
+CFLAGS = $(OPTS) $(WARNINGS) -fPIC
+CPPFLAGS += -DO2CB_FLAT_INCLUDES
+
+ifneq ($(OCFS2_DEBUG_EXE),)
+DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES))
+DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES)))
+
+.SECONDARY:
+
+UNINST_PROGRAMS += $(DEBUG_EXE_PROGRAMS)
+
+debug_%.o : %.c 
+	$(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \
+		$(INCLUDES) $(DEFINES) \
+		-DDEBUG_EXE -o $@ -c $<
+
+debug_%: debug_%.o libo2cb.a
+	$(LINK) $(COM_ERR_LIBS)
+
+endif
+
+CFILES = 		\
+	o2cb_abi.c
+
+HFILES =				\
+	include/ocfs2_heartbeat.h	\
+	include/ocfs2_nodemanager.h	\
+	include/ocfs2_tcp.h.h		\
+	include/o2cb_abi.h		\
+	include/o2cb.h
+
+HFILES_GEN =		\
+	include/o2cb_err.h
+	
+$(CFILES): $(HFILES_GEN)
+
+OBJS = $(subst .c,.o,$(CFILES)) \
+	o2cb_err.o
+
+o2cb_err.et: o2cb_err.et.in
+	cd $(TOPDIR) && ./config.status
+
+include/o2cb_err.h: o2cb_err.h
+	cp $< $@
+
+o2cb_err.c o2cb_err.h: o2cb_err.et
+	compile_et o2cb_err.et
+
+libo2cb.a: $(OBJS)
+	rm -f $@
+	$(AR) r $@ $^
+	$(RANLIB) $@
+
+DIST_FILES = $(CFILES) $(HFILES) o2cb_err.et.in
+
+DIST_RULES = dist-subdircreate
+
+dist-subdircreate:
+	$(TOPDIR)/mkinstalldirs $(DIST_DIR)/include
+
+CLEAN_RULES = clean-err
+
+clean-err:
+	rm -f o2cb_err.c o2cb_err.h include/o2cb_err.h
+
+include $(TOPDIR)/Postamble.make


Property changes on: trunk/libo2cb/include
___________________________________________________________________
Name: svn:ignore
   + .*.sw?
o2cb_err.h


Added: trunk/libo2cb/include/o2cb.h
===================================================================
--- trunk/libo2cb/include/o2cb.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/include/o2cb.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,58 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * o2cb.h
+ *
+ * Routines for accessing the o2cb configuration.
+ *
+ * Copyright (C) 2004 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License, version 2,  as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef _O2CB_H
+#define _O2CB_H
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef _LARGEFILE64_SOURCE
+# define _LARGEFILE64_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <linux/types.h>
+
+#include <et/com_err.h>
+
+#if O2CB_FLAT_INCLUDES
+#include "o2cb_err.h"
+
+#include "ocfs2_heartbeat.h"
+#include "ocfs2_nodemanager.h"
+#include "ocfs2_tcp.h"
+#else
+#include <o2cb/o2cb_err.h>
+
+#include <o2cb/ocfs2_heartbeat.h>
+#include <o2cb/ocfs2_nodemanager.h>
+#include <o2cb/ocfs2_tcp.h>
+#endif
+
+#endif  /* _O2CB_H */

Added: trunk/libo2cb/include/o2cb_abi.h
===================================================================
--- trunk/libo2cb/include/o2cb_abi.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/include/o2cb_abi.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,35 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * o2cb_abi.c
+ *
+ * Kernel<->User ABI for modifying cluster configuration.
+ *
+ * Copyright (C) 2004 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License, version 2,  as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef _O2CB_ABI_H
+#define _O2CB_ABI_H
+
+/* Hardcoded paths to the cluster virtual files */
+#define O2CB_CLUSTER_FILE 	"/proc/cluster/nm/.cluster"
+#define O2CB_GROUP_FILE		"/proc/cluster/nm/.group"
+#define O2CB_NODE_FILE		"/proc/cluster/nm/.node"
+
+int o2cb_set_cluster_name(const char *cluster_name);
+
+#endif  /* _O2CB_ABI_H */

Modified: trunk/libo2cb/include/ocfs2_heartbeat.h
===================================================================
--- trunk/libo2cb/include/ocfs2_heartbeat.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/include/ocfs2_heartbeat.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -46,14 +46,14 @@
 
 typedef struct _hb_op
 {
-	u16 magic;
-	u16 opcode;
+	__u16 magic;
+	__u16 opcode;
 	unsigned int fd;
 	char disk_uuid[CLUSTER_DISK_UUID_LEN+1];
-	u16 group_num;
-	u32 bits;
-	u32 blocks;
-	u64 start;
+	__u16 group_num;
+	__u32 bits;
+	__u32 blocks;
+	__u64 start;
 } hb_op;
 
 enum {
@@ -79,7 +79,7 @@
 
 typedef struct _hb_disk_heartbeat_block
 {
-	u64 time;
+	__u64 time;
 } hb_disk_heartbeat_block;
 
 

Modified: trunk/libo2cb/include/ocfs2_nodemanager.h
===================================================================
--- trunk/libo2cb/include/ocfs2_nodemanager.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/include/ocfs2_nodemanager.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -64,17 +64,17 @@
 
 typedef struct _nm_network_iface
 {
-	u16 ip_port;			/* for simplicity, just define exactly one port for this if */
-	u16 ip_version;
+	__u16 ip_port;			/* for simplicity, just define exactly one port for this if */
+	__u16 ip_version;
 	union {
-		u32 ip_addr4;		/* IPv4 address in NBO */
-		u32 ip_addr6[4];	/* IPv6 address in NBO */
+		__u32 ip_addr4;		/* IPv4 address in NBO */
+		__u32 ip_addr6[4];	/* IPv6 address in NBO */
 	} addr_u;
 } nm_network_iface;
 
 typedef struct _nm_node_info 
 {
-	u16 node_num;
+	__u16 node_num;
 	char node_name[NM_MAX_NAME_LEN+1];
 	nm_network_iface ifaces[NM_MAX_IFACES];
 } nm_node_info;
@@ -97,19 +97,19 @@
 
 typedef struct _nm_group_change
 {
-	u16 group_num;
-	u16 node_num;
-	u16 slot_num;
+	__u16 group_num;
+	__u16 node_num;
+	__u16 slot_num;
 	char disk_uuid[CLUSTER_DISK_UUID_LEN+1];
 	char name[NM_MAX_NAME_LEN+1];
 } nm_group_change;
 
 typedef struct _nm_op
 {
-	u16 magic;
-	u16 opcode;
+	__u16 magic;
+	__u16 opcode;
 	union {
-		u16 index;
+		__u16 index;
 		char name[NM_MAX_NAME_LEN+1];
 		nm_node_info node;
 		nm_group_change gc;

Modified: trunk/libo2cb/include/ocfs2_tcp.h
===================================================================
--- trunk/libo2cb/include/ocfs2_tcp.h	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/include/ocfs2_tcp.h	2004-12-14 23:59:14 UTC (rev 490)
@@ -52,10 +52,10 @@
 #define GSD_ACTION_ADD_GROUP_NODE   (0x02)
 typedef struct _gsd_message
 {
-	u16 from;
-	u8 action;
-	u8 namelen;
-	u8 name[NM_MAX_NAME_LEN];
+	__u16 from;
+	__u8 action;
+	__u8 namelen;
+	__u8 name[NM_MAX_NAME_LEN];
 } gsd_message;
 
 #endif /* DLMNET_H */

Added: trunk/libo2cb/o2cb_abi.c
===================================================================
--- trunk/libo2cb/o2cb_abi.c	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/o2cb_abi.c	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,95 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * o2cb_abi.c
+ *
+ * Kernel<->User ABI for modifying cluster configuration.
+ *
+ * Copyright (C) 2004 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License, version 2,  as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#define _XOPEN_SOURCE 600  /* Triggers XOPEN2K in features.h */
+#define _LARGEFILE64_SOURCE
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/types.h>
+
+#include "o2cb.h"
+
+#include "o2cb_abi.h"
+
+int o2cb_set_cluster_name(const char *cluster_name)
+{
+	int fd, rc, page_size = getpagesize();
+	char *buf;
+	nm_op *op;
+
+	if (strlen(cluster_name) > NM_MAX_NAME_LEN)
+		return O2CB_ET_INVALID_CLUSTER_NAME;
+
+	buf = malloc(sizeof(char*) * page_size);
+	if (!buf)
+		return -errno;
+
+	op = (nm_op *)buf;
+	op->magic = NM_OP_MAGIC;
+	op->opcode = NM_OP_NAME_CLUSTER;
+	strcpy(op->arg_u.name, cluster_name);
+
+	fd = open(O2CB_CLUSTER_FILE, O_RDWR);
+	if (fd < 0) {
+		rc = -errno;
+		goto out;
+	}
+
+	rc = write(fd, op, sizeof(nm_op));
+	if (rc < 0) {
+		rc = -errno;
+		goto out_close;
+	} else if (rc < sizeof(nm_op)) {
+		/* FIXME: What to do here? */
+	}
+
+	memset(buf, 0, page_size);
+	rc = read(fd, buf, page_size);
+	if (rc < 0) {
+		rc = -errno;
+		goto out_close;
+	} else if (!rc) {
+		/* FIXME: What to do here? */
+	} else {
+		if (buf[0] == '\0')
+			rc = 0;
+		/* FIXME: genericize, make better, etc */
+	}
+
+out_close:
+	close(fd);
+out:
+	free(buf);
+
+	return rc;
+}  /* o2cb_set_cluster_name() */
+

Added: trunk/libo2cb/o2cb_err.et.in
===================================================================
--- trunk/libo2cb/o2cb_err.et.in	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/libo2cb/o2cb_err.et.in	2004-12-14 23:59:14 UTC (rev 490)
@@ -0,0 +1,30 @@
+#
+# o2cb_err.et.in
+#
+# Error codes for the O2CB library.
+#
+# Copyright (C) 2004 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public
+# License, version 2,  as published by the Free Software Foundation.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 021110-1307, USA.
+#
+	error_table o2cb
+
+ec	O2CB_ET_BASE,
+	"O2CB Library version @VERSION@"
+
+ec	O2CB_ET_INVALID_CLUSTER_NAME,
+	"Invalid name for a cluster"
+
+	end

Modified: trunk/mount.ocfs2/Makefile
===================================================================
--- trunk/mount.ocfs2/Makefile	2004-12-14 23:50:34 UTC (rev 489)
+++ trunk/mount.ocfs2/Makefile	2004-12-14 23:59:14 UTC (rev 490)
@@ -5,7 +5,7 @@
 SBIN_PROGRAMS = mount.ocfs2
 
 INCLUDES = -Iinclude -I$(TOPDIR)/libocfs2/include \
-	    -I$(TOPDIR)/libocfs2cluster/include
+	    -I$(TOPDIR)/libo2cb/include
 LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2
 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a
 



More information about the Ocfs2-tools-commits mailing list