[Endpoint-commits] rev 2 - in trunk: . libjconfig librbc libsbp2 src

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Thu Jul 17 20:27:11 CDT 2003


Author: manish
Date: 2003-07-17 14:27:07 -0500 (Thu, 17 Jul 2003)
New Revision: 2

Added:
   trunk/AUTHORS
   trunk/COPYING
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/NEWS
   trunk/README
   trunk/autogen.sh
   trunk/configure.in
   trunk/libjconfig/
   trunk/libjconfig/Makefile.am
   trunk/libjconfig/jconfig.c
   trunk/libjconfig/jconfig.h
   trunk/libjconfig/jiterator.c
   trunk/libjconfig/jiterator.h
   trunk/librbc/
   trunk/librbc/Makefile.am
   trunk/librbc/rbcbuffer.c
   trunk/librbc/rbcbuffer.h
   trunk/librbc/rbccommand.c
   trunk/librbc/rbccommand.h
   trunk/librbc/rbcdisk.c
   trunk/librbc/rbcdisk.h
   trunk/librbc/rbcprivate.h
   trunk/librbc/rbctypes.h
   trunk/libsbp2/
   trunk/libsbp2/Makefile.am
   trunk/libsbp2/sbp2byteswap.h
   trunk/libsbp2/sbp2configrom.c
   trunk/libsbp2/sbp2configrom.h
   trunk/libsbp2/sbp2constants.h
   trunk/libsbp2/sbp2csr.h
   trunk/libsbp2/sbp2login.h
   trunk/libsbp2/sbp2logout.h
   trunk/libsbp2/sbp2main.c
   trunk/libsbp2/sbp2main.h
   trunk/libsbp2/sbp2manager.c
   trunk/libsbp2/sbp2manager.h
   trunk/libsbp2/sbp2pagetable.h
   trunk/libsbp2/sbp2pointer.h
   trunk/libsbp2/sbp2querylogins.h
   trunk/libsbp2/sbp2raw1394.c
   trunk/libsbp2/sbp2raw1394.h
   trunk/libsbp2/sbp2reconnect.h
   trunk/libsbp2/sbp2scsicommand.h
   trunk/libsbp2/sbp2scsistatus.h
   trunk/libsbp2/sbp2status.h
   trunk/libsbp2/sbp2worker.c
   trunk/libsbp2/sbp2worker.h
   trunk/src/
   trunk/src/Makefile.am
   trunk/src/app.c
   trunk/src/app.h
   trunk/src/cleanup.c
   trunk/src/cleanup.h
   trunk/src/main.c
   trunk/src/manager.c
   trunk/src/manager.h
   trunk/src/process.c
   trunk/src/process.h
   trunk/src/util.c
   trunk/src/util.h
   trunk/src/wire.c
   trunk/src/wire.h
   trunk/src/worker.c
   trunk/src/worker.h
Log:
Initial import


Added: trunk/AUTHORS
==============================================================================
--- trunk/AUTHORS	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/AUTHORS	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1 @@
+Manish Singh <manish.singh at oracle.com>

Added: trunk/COPYING
==============================================================================
--- trunk/COPYING	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/COPYING	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    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  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: trunk/ChangeLog
==============================================================================
--- trunk/ChangeLog	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/ChangeLog	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,3 @@
+Thu Jul 17 12:19:36 2003  Manish Singh  <manish.singh at oracle.com>
+
+	* Initial import

Added: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/Makefile.am	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1 @@
+SUBDIRS = libjconfig librbc libsbp2 src

Added: trunk/NEWS
==============================================================================

Added: trunk/README
==============================================================================
--- trunk/README	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/README	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,4 @@
+Endpoint turns a Linux machine with one or more firewire cards into an SBP-2
+device. The advantage is that you can use any file or device the linux
+machine supports as storage, and that multiple clients can sit on separate
+buses so they can get the full bandwidth of the bus.

Added: trunk/autogen.sh
==============================================================================
--- trunk/autogen.sh	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/autogen.sh	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+aclocal-1.7 $ACLOCAL_FLAGS
+autoheader
+automake-1.7 -a
+autoconf
+
+./configure --enable-maintainer-mode "$@"

Added: trunk/configure.in
==============================================================================
--- trunk/configure.in	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/configure.in	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,52 @@
+AC_PREREQ(2.53)
+
+AC_INIT(libsbp2/sbp2manager.c)
+
+PACKAGE=endpoint
+
+ENDPOINT_MAJOR_VERSION=0
+ENDPOINT_MINOR_VERSION=1
+ENDPOINT_MICRO_VERSION=0
+
+VERSION=$ENDPOINT_MAJOR_VERSION.$ENDPOINT_MINOR_VERSION.$ENDPOINT_MICRO_VERSION
+
+AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define)
+
+AM_CONFIG_HEADER(config.h)
+
+AM_MAINTAINER_MODE
+
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+AC_PROG_CC
+AC_PROG_CPP
+
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+
+AC_PROG_RANLIB
+
+if eval "test x$GCC = xyes"; then
+  case " $CFLAGS " in
+  *[\ \	]-Wall[\ \	]*) ;;
+  *) CFLAGS="$CFLAGS -Wall" ;;
+  esac
+fi
+
+AC_LIB_RAW1394(0.9.0,,
+   AC_MSG_ERROR([Could not find libraw1394]))
+
+PKG_CHECK_MODULES(GLIB, glib-2.0,,
+  AC_MSG_ERROR([Could not find GLIB 2.x]))
+
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+AC_OUTPUT([
+Makefile
+libjconfig/Makefile
+librbc/Makefile
+libsbp2/Makefile
+src/Makefile
+])

Added: trunk/libjconfig/Makefile.am
==============================================================================
--- trunk/libjconfig/Makefile.am	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libjconfig/Makefile.am	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,12 @@
+noinst_LIBRARIES = libjconfig.a
+
+INCLUDES = \
+	-I$(top_srcdir)	\
+	$(GLIB_CFLAGS)	\
+	-I$(includedir)
+	
+libjconfig_a_SOURCES = \
+	jconfig.c	\
+	jconfig.h	\
+	jiterator.c	\
+	jiterator.h

Added: trunk/libjconfig/jconfig.c
==============================================================================
--- trunk/libjconfig/jconfig.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libjconfig/jconfig.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -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();
+static JConfigStanza *j_config_stanza_new();
+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/libjconfig/jconfig.h
==============================================================================
--- trunk/libjconfig/jconfig.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libjconfig/jconfig.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -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 j_config_context_free(JConfigCtxt *cfc);
+void j_config_set_context_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/libjconfig/jiterator.c
==============================================================================
--- trunk/libjconfig/jiterator.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libjconfig/jiterator.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -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/libjconfig/jiterator.h
==============================================================================
--- trunk/libjconfig/jiterator.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libjconfig/jiterator.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -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 */
+

Added: trunk/librbc/Makefile.am
==============================================================================
--- trunk/librbc/Makefile.am	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/Makefile.am	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,16 @@
+noinst_LIBRARIES = librbc.a
+
+INCLUDES = \
+	-I$(top_srcdir)	\
+	$(GLIB_CFLAGS)	\
+	-I$(includedir)
+
+librbc_a_SOURCES = \
+	rbcbuffer.h	\
+	rbcbuffer.c	\
+	rbccommand.h	\
+	rbccommand.c	\
+	rbcdisk.c	\
+	rbcdisk.h	\
+	rbcprivate.h	\
+	rbctypes.h

Added: trunk/librbc/rbcbuffer.c
==============================================================================
--- trunk/librbc/rbcbuffer.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbcbuffer.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,121 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <glib.h>
+
+#include "rbcbuffer.h"
+
+
+static inline RBCBuffer *
+alloc_buffer (RBCBufferType type)
+{
+  RBCBuffer *buffer;
+
+  buffer = g_new (RBCBuffer, 1);
+  buffer->type = type;
+
+  return buffer;
+}
+
+
+RBCBuffer *
+rbc_buffer_empty_new (void)
+{
+  return alloc_buffer (RBC_BUFFER_EMPTY);
+}
+
+RBCBuffer *
+rbc_buffer_simple_new (gsize length)
+{
+  RBCBuffer *buffer;
+
+  g_return_val_if_fail (length > 0, NULL);
+
+  buffer = alloc_buffer (RBC_BUFFER_SIMPLE);
+
+  buffer->length = length;
+  buffer->data = g_malloc (length);
+
+  return buffer;
+}
+
+RBCBuffer *
+rbc_buffer_page_table_new (gsize length)
+{
+  RBCBuffer *buffer;
+
+  g_return_val_if_fail (length > 0, NULL);
+
+  buffer = alloc_buffer (RBC_BUFFER_PAGE_TABLE);
+
+  buffer->length = length;
+  buffer->data = g_new (RBCPage, length);
+
+  return buffer;
+}
+
+void
+rbc_buffer_destroy (RBCBuffer *buffer)
+{
+  RBCPage *pages;
+  gint     i;
+
+  g_return_if_fail (buffer != NULL);
+
+  switch (buffer->type)
+    {
+
+    case RBC_BUFFER_PAGE_TABLE:
+      pages = buffer->data;
+
+      for (i = 0; i < buffer->length; i++)
+	g_free (pages[i].data);
+
+      /* fall through */
+
+    case RBC_BUFFER_SIMPLE:
+      g_free (buffer->data);
+      /* fall through */
+
+    case RBC_BUFFER_EMPTY:
+      g_free (buffer);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+void
+rbc_buffer_fill_from_page (RBCBuffer *buffer,
+                           RBCPage   *page)
+{
+  g_return_if_fail (buffer != NULL);
+  g_return_if_fail (page != NULL);
+
+  buffer->type = RBC_BUFFER_SIMPLE;
+  buffer->length = page->length;
+  buffer->data = page->data;
+}
+

Added: trunk/librbc/rbcbuffer.h
==============================================================================
--- trunk/librbc/rbcbuffer.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbcbuffer.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,70 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __RBC_BUFFER_H__
+#define __RBC_BUFFER_H__
+
+
+#include <glib.h>
+
+
+typedef enum
+{
+  RBC_BUFFER_EMPTY,
+  RBC_BUFFER_SIMPLE,
+  RBC_BUFFER_PAGE_TABLE
+} RBCBufferType;
+
+
+typedef struct _RBCBuffer RBCBuffer;
+typedef struct _RBCPage   RBCPage;
+
+struct _RBCBuffer
+{
+  RBCBufferType type;
+
+  gpointer      data;
+  gsize         length;
+};
+
+/* This needs to match struct iovec for readv/writev */
+struct _RBCPage
+{
+  gpointer data;
+  gsize    length;
+};
+
+
+RBCBuffer *rbc_buffer_empty_new      (void);
+
+RBCBuffer *rbc_buffer_simple_new     (gsize      length);
+
+RBCBuffer *rbc_buffer_page_table_new (gsize      length);
+
+void       rbc_buffer_destroy        (RBCBuffer *buffer);
+
+void       rbc_buffer_fill_from_page (RBCBuffer *buffer,
+                                      RBCPage   *page);
+
+
+#endif /* __RBC_BUFFER_H__ */

Added: trunk/librbc/rbccommand.c
==============================================================================
--- trunk/librbc/rbccommand.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbccommand.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,416 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include <glib.h>
+
+#include "rbccommand.h"
+#include "rbcprivate.h"
+
+
+#define VENDOR_ID   "Linux   "
+#define PRODUCT_ID  "Endpoint        "
+#define PRODUCT_REV "0001"
+
+
+typedef enum
+{
+  INQUIRY         = 0x12,
+  MODE_SELECT     = 0x15,
+  MODE_SENSE      = 0x1a,
+  READ            = 0x28,
+  READ_CAPACITY   = 0x25,
+  START_STOP_UNIT = 0x1b,
+  TEST_UNIT_READY = 0x00,
+  VERIFY          = 0x2f,
+  WRITE           = 0x2a,
+  WRITE_BUFFER    = 0x3b
+} Command;
+
+typedef enum
+{
+  GOOD                 = 0x0,
+  CHECK_CONDITION      = 0x2,
+  CONDITION_MET        = 0x4,
+  BUSY                 = 0x8,
+  RESERVATION_CONFLICT = 0x18,
+  COMMAND_TERMINATED   = 0x22
+} Status;
+
+typedef enum
+{
+  NO_SENSE        = 0x00,
+  RECOVERED_ERROR = 0x01,
+  NOT_READY       = 0x02,
+  MEDIUM_ERROR    = 0x03,
+  ILLEGAL_REQUEST = 0x04,
+  UNIT_ATTENTION  = 0x05,
+  DATA_PROTECT    = 0x06,
+  BLANK_CHECK     = 0x08,
+  COPY_ABORTED    = 0x0a,
+  ABORTED_COMMAND = 0x0b,
+  VOLUME_OVERFLOW = 0x0d,
+  MISCOMPARE      = 0x0e
+} SenseKey;
+
+
+typedef gboolean (*CommandHandler) (RBCDisk   *disk,
+				    guint8    *cmd,
+				    RBCBuffer *buffer,
+				    guint8    *sense);
+
+
+static gboolean handle_inquiry         (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_mode_select     (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_mode_sense      (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_read            (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_read_capacity   (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_start_stop_unit (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_test_unit_ready (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_verify          (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_write           (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+static gboolean handle_write_buffer    (RBCDisk   *disk,
+					guint8    *cmd,
+					RBCBuffer *buffer,
+					guint8    *sense);
+
+
+static inline void
+fill_sense_buffer (guint8   *sense,
+                   Status    status,
+                   SenseKey  key,
+		   gint      asc,
+		   gint      asq)
+{
+  g_return_if_fail (sense != NULL);
+
+  sense[0] = status;
+  sense[1] = key;
+  sense[2] = asc;
+  sense[3] = asq;
+}
+
+
+gboolean
+rbc_disk_command (RBCDisk   *disk,
+		  guint8    *cmd,
+		  RBCBuffer *buffer,
+		  guint8    *sense)
+{
+  CommandHandler handler;
+  RBCBuffer      sg_buffer;
+  gboolean       process_sg = TRUE;
+  gboolean       no_empty = TRUE;
+
+  g_return_val_if_fail (disk != NULL, FALSE);
+  g_return_val_if_fail (cmd != NULL, FALSE);
+  g_return_val_if_fail (buffer != NULL, FALSE);
+  g_return_val_if_fail (sense != NULL, FALSE);
+
+  switch (cmd[0])
+    {
+    case INQUIRY:
+      handler = handle_inquiry;
+      break;
+
+    case MODE_SELECT:
+      handler = handle_mode_select;
+      no_empty = FALSE;
+      break;
+
+    case MODE_SENSE:
+      handler = handle_mode_sense;
+      no_empty = FALSE;
+      break;
+
+    case READ:
+      handler = handle_read;
+      process_sg = FALSE;
+      break;
+
+    case READ_CAPACITY:
+      handler = handle_read_capacity;
+      break;
+
+    case START_STOP_UNIT:
+      handler = handle_start_stop_unit;
+      no_empty = FALSE;
+      break;
+
+    case TEST_UNIT_READY:
+      handler = handle_test_unit_ready;
+      no_empty = FALSE;
+      break;
+   
+    case VERIFY:
+      handler = handle_verify; 
+      process_sg = FALSE;
+      break;
+
+    case WRITE:
+      handler = handle_write;
+      process_sg = FALSE;
+      break;
+
+    case WRITE_BUFFER:
+      handler = handle_write_buffer;
+      no_empty = FALSE;
+      break;
+
+    default:
+      fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x20, 0);
+      return FALSE;
+      break;
+    }
+
+  if (no_empty && buffer->type == RBC_BUFFER_EMPTY)
+    {
+      fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x20, 0);
+      return FALSE;
+    }
+
+  if (process_sg)
+    {
+      if (buffer->type == RBC_BUFFER_PAGE_TABLE)
+	{
+	  RBCPage *page = buffer->data;
+
+	  rbc_buffer_fill_from_page (&sg_buffer, page);
+	  buffer = &sg_buffer;
+	}
+    }
+
+  return handler (disk, cmd, buffer, sense);
+}
+
+static gboolean
+handle_inquiry (RBCDisk   *disk,
+		guint8    *cmd,
+		RBCBuffer *buffer,
+		guint8    *sense)
+{
+  guint8 *data = buffer->data;
+
+  memset (buffer->data, 0, buffer->length);
+
+  data[0] = 0xe;
+
+  memcpy (&data[8],  VENDOR_ID,   8);
+  memcpy (&data[16], PRODUCT_ID,  16);
+  memcpy (&data[32], PRODUCT_REV, 4);
+
+  return TRUE;
+}
+
+static gboolean
+handle_mode_select (RBCDisk   *disk,
+		    guint8    *cmd,
+		    RBCBuffer *buffer,
+		    guint8    *sense)
+{
+  /* TODO: implement */
+
+  fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x20, 0);
+  return FALSE;
+}
+
+static gboolean
+handle_mode_sense (RBCDisk   *disk,
+		   guint8    *cmd,
+		   RBCBuffer *buffer,
+		   guint8    *sense)
+{
+  /* TODO: implement */
+
+  fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x20, 0);
+  return FALSE;
+}
+
+static gboolean
+handle_read (RBCDisk   *disk,
+	     guint8    *cmd,
+	     RBCBuffer *buffer,
+	     guint8    *sense)
+{
+  gsize block;
+  gsize num;
+  off_t offset;
+  gsize length;
+
+  block = (cmd[2] << 24) + (cmd[3] << 16) + (cmd[4] << 8) + cmd[5];
+  num = (cmd[7] << 8) + cmd[8];
+
+  if (block + num > disk->capacity)
+    {
+      fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x21, 0);
+      return FALSE;
+    }
+
+  if (num == 0)
+    return TRUE;
+
+  offset = (off_t) block * RBC_BLOCK_SIZE;
+  length = num * RBC_BLOCK_SIZE;
+
+  if (buffer->type == RBC_BUFFER_PAGE_TABLE)
+    {
+      lseek (disk->fd, offset, SEEK_SET);
+      readv (disk->fd, buffer->data, buffer->length);
+    }
+  else
+    pread (disk->fd, buffer->data, length, offset);
+
+  return TRUE;
+}
+
+static gboolean
+handle_read_capacity (RBCDisk   *disk,
+		      guint8    *cmd,
+		      RBCBuffer *buffer,
+		      guint8    *sense)
+{
+  guint8 *data = buffer->data;
+  gsize capacity = disk->capacity - 1;
+
+  memset (buffer->data, 0, buffer->length);
+
+  data[0] = (capacity >> 24);
+  data[1] = (capacity >> 16) & 0xff;
+  data[2] = (capacity >> 8) & 0xff;
+  data[3] = capacity & 0xff;
+  data[6] = (RBC_BLOCK_SIZE >> 8) & 0xff;
+  data[7] = RBC_BLOCK_SIZE & 0xff;
+
+  return TRUE;
+}
+
+static gboolean
+handle_start_stop_unit (RBCDisk   *disk,
+			guint8    *cmd,
+			RBCBuffer *buffer,
+			guint8    *sense)
+{
+  /* Power condition requests are unsupported, ignore */
+  return TRUE;
+}
+
+static gboolean
+handle_test_unit_ready (RBCDisk   *disk,
+			guint8    *cmd,
+			RBCBuffer *buffer,
+			guint8    *sense)
+{
+  /* We're always ready */
+  return TRUE;
+}
+
+static gboolean
+handle_verify (RBCDisk   *disk,
+	       guint8    *cmd,
+	       RBCBuffer *buffer,
+	       guint8    *sense)
+{
+  /* TODO: Implement */
+  return TRUE;
+}
+
+static gboolean
+handle_write (RBCDisk   *disk,
+	      guint8    *cmd,
+	      RBCBuffer *buffer,
+	      guint8    *sense)
+{
+  gsize block;
+  gsize num;
+  off_t offset;
+  gsize length;
+
+  block = (cmd[2] << 24) + (cmd[3] << 16) + (cmd[4] << 8) + cmd[5];
+  num = (cmd[7] << 8) + cmd[8];
+
+  if (block + num > disk->capacity)
+    {
+      fill_sense_buffer (sense, CHECK_CONDITION, ILLEGAL_REQUEST, 0x21, 0);
+      return FALSE;
+    }
+
+  if (num == 0)
+    return TRUE;
+
+  offset = (off_t) block * RBC_BLOCK_SIZE;
+  length = num * RBC_BLOCK_SIZE;
+
+  if (buffer->type == RBC_BUFFER_PAGE_TABLE)
+    {
+      lseek (disk->fd, offset, SEEK_SET);
+      writev (disk->fd, buffer->data, buffer->length);
+    }
+  else
+    pwrite (disk->fd, buffer->data, length, offset);
+
+  return TRUE;
+}
+
+static gboolean 
+handle_write_buffer (RBCDisk   *disk,
+		     guint8    *cmd,
+		     RBCBuffer *buffer,
+		     guint8    *sense)
+{
+  /* Unsupported, always succeed */
+  return TRUE;
+}

Added: trunk/librbc/rbccommand.h
==============================================================================
--- trunk/librbc/rbccommand.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbccommand.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,40 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __RBC_COMMAND_H__
+#define __RBC_COMMAND_H__
+
+
+#include <glib.h>
+
+#include <librbc/rbcbuffer.h>
+#include <librbc/rbcdisk.h>
+
+
+gboolean rbc_disk_command (RBCDisk   *disk,
+                           guint8    *cmd,
+			   RBCBuffer *buffer,
+			   guint8    *sense);
+
+
+#endif /* __RBC_COMMAND_H__ */

Added: trunk/librbc/rbcdisk.c
==============================================================================
--- trunk/librbc/rbcdisk.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbcdisk.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,286 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "rbcdisk.h"
+#include "rbcprivate.h"
+
+
+static gsize process_filename (const gchar        *filename,
+			       gint               *out_fd);
+
+static gint  partition_order  (const RBCPartition *a,
+			       const RBCPartition *b);
+
+
+RBCDisk *
+rbc_disk_new (RBCDiskType type)
+{
+  RBCDisk *disk;
+
+  disk = g_new (RBCDisk, 1);
+
+  disk->type = type;
+
+  disk->filename = NULL;
+  disk->partitions = NULL;
+
+  disk->fd = -1;
+  disk->capacity = 0;
+
+  disk->active = FALSE;
+
+  return disk;
+}
+
+void
+rbc_disk_destroy (RBCDisk *disk)
+{
+  GList        *list;
+  RBCPartition *partition;
+
+  g_return_if_fail (disk != NULL);
+
+  if (disk->fd != -1)
+    close (disk->fd);
+
+  if (disk->filename)
+    g_free (disk->filename);
+
+  if (disk->partitions)
+    {
+      list = disk->partitions;
+      
+      while (list)
+	{
+	  partition = list->data;
+
+	  if (partition->fd != -1)
+	    close (partition->fd);
+
+	  if (partition->filename)
+	    g_free (partition->filename);
+
+	  list = list->next;
+	}
+
+      g_list_free (disk->partitions);
+    }
+
+  g_free (disk);
+}
+
+RBCDiskType
+rbc_disk_get_type (RBCDisk *disk)
+{
+  g_return_val_if_fail (disk != NULL, RBC_DISK_PHYSICAL);
+
+  return disk->type;
+}
+
+gboolean
+rbc_device_is_equal (RBCDisk *a,
+                     RBCDisk *b)
+{
+  g_return_val_if_fail (a != NULL, FALSE);
+  g_return_val_if_fail (b != NULL, FALSE);
+
+  /* TODO: implement */
+
+  return a == b;
+}
+
+gboolean
+rbc_disk_set_filename (RBCDisk     *disk,
+                       const gchar *filename)
+{
+  gsize capacity;
+
+  g_return_val_if_fail (disk != NULL, FALSE);
+  g_return_val_if_fail (disk->type == RBC_DISK_PHYSICAL, FALSE);
+  g_return_val_if_fail (disk->active == FALSE, FALSE);
+  g_return_val_if_fail (filename != NULL, FALSE);
+  g_return_val_if_fail (disk->filename == NULL, FALSE);
+
+  capacity = process_filename (filename, NULL);
+
+  if (capacity == 0)
+    return FALSE;
+
+  disk->filename = g_strdup (filename);
+  disk->capacity = capacity;
+
+  return TRUE;
+}
+
+gboolean
+rbc_disk_add_partition (RBCDisk     *disk,
+                        const gchar *filename,
+			guint        order)
+{
+  gsize         capacity;
+  RBCPartition *partition;
+
+  g_return_val_if_fail (disk != NULL, FALSE);
+  g_return_val_if_fail (disk->type == RBC_DISK_VIRTUAL, FALSE);
+  g_return_val_if_fail (disk->active == FALSE, FALSE);
+  g_return_val_if_fail (filename != NULL, FALSE);
+
+  capacity = process_filename (filename, NULL);
+
+  if (capacity == 0)
+    return FALSE;
+
+  partition = g_new (RBCPartition, 1);
+
+  partition->filename = g_strdup (filename);
+  partition->order = order;
+
+  partition->fd = -1;
+  partition->capacity = capacity;
+
+  disk->partitions = g_list_insert_sorted (disk->partitions, partition,
+                                           (GCompareFunc) partition_order);
+
+  return TRUE;
+}
+
+gsize
+rbc_disk_get_capacity (RBCDisk *disk)
+{
+  g_return_val_if_fail (disk != NULL, 0);
+
+  return disk->capacity;
+}
+
+gsize
+rbc_disk_get_block_size (RBCDisk *disk)
+{
+  g_return_val_if_fail (disk != NULL, 0);
+
+  return RBC_BLOCK_SIZE;
+}
+
+gboolean
+rbc_disk_activate (RBCDisk *disk)
+{
+  GList        *list;
+  RBCPartition *partition;
+
+  g_return_val_if_fail (disk != NULL, FALSE);
+
+  switch (disk->type)
+    {
+    case RBC_DISK_PHYSICAL:
+      g_return_val_if_fail (disk->filename != NULL, FALSE);
+
+      disk->capacity = process_filename (disk->filename, &disk->fd);
+
+      if (disk->capacity == 0)
+	return FALSE;
+
+      break;
+
+    case RBC_DISK_VIRTUAL:
+      g_return_val_if_fail (disk->partitions != NULL, FALSE);
+
+      list = disk->partitions;
+
+      while (list)
+	{
+	  partition = list->data;
+
+	  partition->capacity = process_filename (partition->filename,
+	                                          &partition->fd);
+
+	  if (partition->capacity == 0)
+	    goto cleanup;
+
+	  list = list->next;
+	}
+
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return TRUE;
+  
+cleanup:
+  list = list->prev;
+
+  while (list)
+    {
+      partition = list->data;
+
+      close (partition->fd);
+
+      list = list->prev;
+    }
+
+  return FALSE;
+}
+
+static gsize
+process_filename (const gchar *filename,
+                  gint        *out_fd)
+{
+  gint  fd;
+  gsize capacity;
+
+  g_return_val_if_fail (filename != NULL, 0);
+
+  fd = open (filename, O_RDWR);
+
+  if (fd == -1)
+    return 0;
+
+  capacity = lseek (fd, 0, SEEK_END) / RBC_BLOCK_SIZE;
+
+  if (out_fd)
+    *out_fd = fd;
+  else
+    close (fd);
+
+  return capacity;
+}
+
+static gint
+partition_order (const RBCPartition *a,
+                 const RBCPartition *b)
+{
+  if (a->order < b->order)
+    return -1;
+  else if (a->order > b->order)
+    return 1;
+
+  return 0;
+}

Added: trunk/librbc/rbcdisk.h
==============================================================================
--- trunk/librbc/rbcdisk.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbcdisk.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,55 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __RBC_DISK_H__
+#define __RBC_DISK_H__
+
+
+#include <glib.h>
+
+#include <librbc/rbctypes.h>
+
+
+RBCDisk     *rbc_disk_new            (RBCDiskType  type);
+
+void         rbc_disk_destroy        (RBCDisk     *disk);
+
+RBCDiskType  rbc_disk_get_type       (RBCDisk     *disk);
+
+gboolean     rbc_disk_is_equal       (RBCDisk     *a,
+				      RBCDisk     *b);
+
+gboolean     rbc_disk_set_filename   (RBCDisk     *disk,
+                                      const gchar *filename);
+
+gboolean     rbc_disk_add_partition  (RBCDisk     *disk,
+				      const gchar *filename,
+				      guint        order);
+
+gsize        rbc_disk_get_capacity   (RBCDisk     *disk);
+gsize        rbc_disk_get_block_size (RBCDisk     *disk);
+
+gboolean     rbc_disk_activate       (RBCDisk     *disk);
+
+
+#endif /* __RBC_DISK_H__ */

Added: trunk/librbc/rbcprivate.h
==============================================================================
--- trunk/librbc/rbcprivate.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbcprivate.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,62 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __RBC_PRIVATE_H__
+#define __RBC_PRIVATE_H__
+
+
+#include <glib.h>
+
+#include <librbc/rbctypes.h>
+
+
+#define RBC_BLOCK_SIZE 512
+
+
+typedef struct _RBCPartition RBCPartition;
+
+struct _RBCPartition
+{
+  gchar *filename;
+  guint  order;
+
+  gint   fd;
+  gsize  capacity;
+};
+
+struct _RBCDisk
+{
+  RBCDiskType  type;
+
+  gchar       *filename;
+
+  GList       *partitions;
+
+  gint         fd;
+  gsize        capacity;
+
+  gboolean     active;
+};
+
+
+#endif /* __RBC_PRIVATE_H__ */

Added: trunk/librbc/rbctypes.h
==============================================================================
--- trunk/librbc/rbctypes.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/librbc/rbctypes.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,38 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __RBC_TYPES_H__
+#define __RBC_TYPES_H__
+
+
+typedef enum
+{
+  RBC_DISK_PHYSICAL,
+  RBC_DISK_VIRTUAL
+} RBCDiskType;
+
+
+typedef struct _RBCDisk RBCDisk;
+
+
+#endif /* __RBC_TYPES_H__ */

Added: trunk/libsbp2/Makefile.am
==============================================================================
--- trunk/libsbp2/Makefile.am	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/Makefile.am	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,31 @@
+noinst_LIBRARIES = libsbp2.a
+
+INCLUDES = \
+	-I$(top_srcdir)		\
+	$(GLIB_CFLAGS)		\
+	$(LIBRAW1394_CPPFLAGS)	\
+	-I$(includedir)
+
+libsbp2_a_SOURCES = \
+	sbp2byteswap.h		\
+	sbp2configrom.c		\
+	sbp2configrom.h		\
+	sbp2constants.h		\
+	sbp2csr.h		\
+	sbp2debug.h		\
+	sbp2manager.c		\
+	sbp2manager.h		\
+	sbp2main.c		\
+	sbp2main.h		\
+	sbp2login.h		\
+	sbp2logout.h		\
+	sbp2pagetable.h		\
+	sbp2pointer.h		\
+	sbp2querylogins.h	\
+	sbp2raw1394.c		\
+	sbp2raw1394.h		\
+	sbp2reconnect.h		\
+	sbp2scsicommand.h	\
+	sbp2status.h		\
+	sbp2worker.c		\
+	sbp2worker.h

Added: trunk/libsbp2/sbp2byteswap.h
==============================================================================
--- trunk/libsbp2/sbp2byteswap.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2byteswap.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,51 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_BYTESWAP_H__
+#define __SBP2_BYTESWAP_H__
+
+
+#include <glib.h>
+
+
+static inline void
+sbp2_byteswap_data (gpointer data,
+                    gint     length)
+{
+  guint32 *temp = data;
+
+  for (length = (length >> 2); length--; )
+    temp[length] = GUINT32_FROM_BE (temp[length]);
+}
+
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define sbp2_be32_to_cpu_data(b, l) sbp2_byteswap_data((b), (l))
+#define sbp2_cpu_to_be32_data(b, l) sbp2_byteswap_data((b), (l))
+#else /* G_BIG_ENDIAN */
+#define sbp2_be32_to_cpu_data(b, l) G_STMT_START { (void)0; } G_STMT_END
+#define sbp2_cpu_to_be32_data(b, l) G_STMT_START { (void)0; } G_STMT_END
+#endif
+
+
+#endif /* __SBP2_BYTESWAP_H__ */

Added: trunk/libsbp2/sbp2configrom.c
==============================================================================
--- trunk/libsbp2/sbp2configrom.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2configrom.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,117 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include "sbp2configrom.h"
+#include "sbp2csr.h"
+
+
+#define ROM_BUF_SIZE 256
+
+
+static guint16 crc16 (guint32 *buffer,
+		      gint     length);
+
+
+gboolean
+sbp2_setup_config_rom (raw1394handle_t handle)
+{
+  quadlet_t rom[ROM_BUF_SIZE];
+  gsize     rom_size;
+  guchar    rom_version;
+  gint      ret, i;
+
+  g_return_val_if_fail (handle != NULL, FALSE);
+
+  ret = raw1394_get_config_rom (handle, rom, ROM_BUF_SIZE,
+                                &rom_size, &rom_version);
+
+  if (ret != 0)
+    return FALSE;
+
+  if (rom[9] == GUINT32_FROM_BE(0xd1000008))
+    return TRUE;
+
+  for (i = 15; i > 8; i--)
+    rom[i + 1] = rom[i];
+
+  rom[7] = GUINT32_FROM_BE(0x81000003);
+  rom[9] = GUINT32_FROM_BE(0xd1000008);
+
+  rom[5] = GUINT32_FROM_BE(4 << 16 | crc16 (&rom[6], 4));
+
+  rom[18] = GUINT32_FROM_BE(0x1200609e);
+  rom[19] = GUINT32_FROM_BE(0x13010483);
+  rom[20] = GUINT32_FROM_BE(0x3800609e); /* IEEE/RAC */
+  rom[21] = GUINT32_FROM_BE(0x390104d8); /* IEEE/RAC */
+  rom[22] = GUINT32_FROM_BE(0x3b000000); /* IEEE/RAC */
+  rom[23] = GUINT32_FROM_BE(0x3c000001); /* IEEE/RAC */
+  rom[24] = GUINT32_FROM_BE(0x54000000 + SBP2_CSR_MANANGEMENT_OFFSET);
+  rom[25] = GUINT32_FROM_BE(0x3a003c08); /* unit characteristics */
+  rom[26] = GUINT32_FROM_BE(0x3d000003); /* reconnect timeout */
+  rom[27] = GUINT32_FROM_BE(0x144e0000); /* logical unit number */
+
+  rom[17] = GUINT32_FROM_BE(10 << 16 | crc16 (&rom[18], 10));
+
+  rom_size += 12 * 4;
+
+  ret = raw1394_update_config_rom (handle, rom, rom_size, rom_version);
+
+  if (ret == -1)
+    return FALSE;
+
+  return TRUE;
+}
+
+static guint16
+crc16 (guint32 *buffer,
+       gint     length)
+{
+  gint    shift;
+  guint32 crc, sum, data;
+
+  g_return_val_if_fail (buffer != NULL, 0);
+
+  crc = 0;
+
+  for (; length > 0; length--)
+    {
+      data = GUINT32_FROM_BE (*buffer);
+
+      for (shift = 28; shift >= 0; shift -= 4)
+	{
+	  sum = ((crc >> 12) ^ (data >> shift)) & 0x000f;
+	  crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
+	}
+
+      crc &= 0xffff;
+
+      buffer++;
+    }
+
+  return crc;
+}
+

Added: trunk/libsbp2/sbp2configrom.h
==============================================================================
--- trunk/libsbp2/sbp2configrom.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2configrom.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,34 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_CONFIGROM_H__
+#define __SBP2_CONFIGROM_H__
+
+
+#include <libraw1394/raw1394.h>
+
+
+gboolean sbp2_setup_config_rom (raw1394handle_t handle);
+
+
+#endif /* __SBP2_CONFIGROM_H__ */

Added: trunk/libsbp2/sbp2constants.h
==============================================================================
--- trunk/libsbp2/sbp2constants.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2constants.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,31 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_CONSTANTS_H__
+#define __SBP2_CONSTANTS_H__
+
+
+#define SBP2_RECONNECT_HOLD 3
+
+
+#endif /* __SBP2_CONSTANTS_H__ */

Added: trunk/libsbp2/sbp2csr.h
==============================================================================
--- trunk/libsbp2/sbp2csr.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2csr.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,36 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_CSR_H__
+#define __SBP2_CSR_H__
+
+
+#define SBP2_CSR_MANANGEMENT_AGENT        0x30000
+#define SBP2_CSR_COMMAND_BLOCK_AGENT_BASE 0x40000 
+
+#define SBP2_CSR_COMMAND_BLOCK_AGENT_SIZE 0x20
+
+#define SBP2_CSR_MANANGEMENT_OFFSET       (SBP2_CSR_MANANGEMENT_AGENT / 4)
+
+
+#endif /* __SBP2_CSR_H__ */

Added: trunk/libsbp2/sbp2login.h
==============================================================================
--- trunk/libsbp2/sbp2login.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2login.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,82 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_LOGIN_H__
+#define __SBP2_LOGIN_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2LoginORB SBP2LoginORB;
+typedef struct _SBP2LoginResponse SBP2LoginResponse;
+
+struct _SBP2LoginORB
+{
+  SBP2Pointer password;
+  SBP2Pointer login_resp;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify          : 1;
+  guint       req_fmt         : 2;
+  guint       exclusive       : 1;
+  guint       reserved        : 4;
+  guint       reconnect       : 4;
+  guint       function        : 4;
+  guint       lun             : 16;
+  guint       password_length : 16;
+  guint       resp_length     : 16;
+#else
+  guint       lun             : 16;
+  guint       function        : 4;
+  guint       reconnect       : 4;
+  guint       reserved        : 4;
+  guint       exclusive       : 1;
+  guint       req_fmt         : 2;
+  guint       notify          : 1;
+  guint       resp_length     : 16;
+  guint       password_length : 16;
+#endif
+  SBP2Pointer status_FIFO;
+};
+
+struct _SBP2LoginResponse
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       length              : 16;
+  guint       login_ID            : 16;
+  SBP2Pointer command_block_agent;
+  guint       reserved            : 16;
+  guint       reconnect_hold      : 16;
+#else
+  guint       login_ID            : 16;
+  guint       length              : 16;
+  SBP2Pointer command_block_agent;
+  guint       reconnect_hold      : 16;
+  guint       reserved            : 16;
+#endif
+};
+
+
+#endif /* __SBP2_LOGIN_H__ */

Added: trunk/libsbp2/sbp2logout.h
==============================================================================
--- trunk/libsbp2/sbp2logout.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2logout.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,59 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_LOGOUT_H__
+#define __SBP2_LOGOUT_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2LogoutORB SBP2LogoutORB;
+
+struct _SBP2LogoutORB
+{
+  guint32     reserved1;
+  guint32     reserved2;
+  guint32     reserved3;
+  guint32     reserved4;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify    : 1;
+  guint       req_fmt   : 2;
+  guint       reserved5 : 9;
+  guint       function  : 4;
+  guint       login_ID  : 16;
+#else
+  guint       login_ID  : 16;
+  guint       function  : 4;
+  guint       reserved5 : 9;
+  guint       req_fmt   : 2;
+  guint       notify    : 1;
+#endif
+  guint32     reserved6;
+  SBP2Pointer status_FIFO;
+};
+
+
+#endif /* __SBP2_LOGOUT_H__ */

Added: trunk/libsbp2/sbp2main.c
==============================================================================
--- trunk/libsbp2/sbp2main.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2main.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,235 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include "sbp2main.h"
+
+
+typedef gint (*ReadWriteFunc) (raw1394handle_t  handle,
+			       nodeid_t         node,
+			       nodeaddr_t       address,
+			       gsize            length,
+			       quadlet_t       *buffer,
+			       gulong           tag);
+
+struct _SBP2Tag
+{
+  guint              ref_count;
+
+  SBP2TagProcessFunc process;
+  SBP2TagFreeFunc    free;
+
+  gpointer           data;
+};
+
+
+static gboolean raw_activity    (GIOChannel        *source,
+				 GIOCondition       condition,
+				 gpointer           data);
+
+static gint     tag_handler     (raw1394handle_t    handle,
+				 gulong             the_tag,
+				 raw1394_errcode_t  errcode);
+
+static void     read_write_data (ReadWriteFunc      read_write_func,
+				 raw1394handle_t    handle,
+				 gsize              chunk_size,
+				 nodeid_t           node,
+				 SBP2Pointer       *pointer,
+				 gpointer           data,
+				 gsize              length,
+				 SBP2Tag           *tag);
+
+
+guint
+sbp2_watch_handle (raw1394handle_t  handle,
+		   GMainContext    *context)
+{
+  gint        fd;
+  GIOChannel *channel;
+  GSource    *source;
+  guint       id;
+
+  g_return_val_if_fail (handle != NULL, 0);
+  g_return_val_if_fail (context != NULL, 0);
+
+  raw1394_set_tag_handler (handle, tag_handler);
+
+  fd = raw1394_get_fd (handle);
+
+  channel = g_io_channel_unix_new (fd);
+
+  g_io_channel_set_encoding (channel, NULL, NULL);
+  g_io_channel_set_buffered (channel, FALSE);
+
+  source = g_io_create_watch (channel, G_IO_IN | G_IO_PRI);
+  g_io_channel_unref (channel);
+
+  g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
+
+  g_source_set_callback (source, (GSourceFunc) raw_activity, handle, NULL);
+  id = g_source_attach (source, context);
+
+  return id;
+}
+
+SBP2Tag *
+sbp2_tag_new (SBP2TagProcessFunc process,
+              SBP2TagFreeFunc    free,
+	      gpointer           data)
+{
+  SBP2Tag *tag;
+
+  tag = g_new (SBP2Tag, 1);
+
+  tag->ref_count = 0;
+
+  tag->process = process;
+  tag->free    = free;
+  tag->data    = data;
+
+  return tag;
+}
+
+void
+sbp2_write_data (raw1394handle_t  handle,
+                 gsize            chunk_size,
+		 nodeid_t         node,
+		 SBP2Pointer     *pointer,
+		 gpointer         data,
+		 gsize            length)
+{
+  SBP2Tag *tag;
+
+  tag = sbp2_tag_new (NULL, g_free, data);
+
+  read_write_data (raw1394_start_write, handle, chunk_size, node, pointer,
+                   data, length, tag);
+}
+
+void
+sbp2_read_data (raw1394handle_t  handle,
+                gsize            chunk_size,
+		nodeid_t         node,
+		SBP2Pointer     *pointer,
+		gpointer         data,
+		gsize            length,
+		SBP2Tag         *tag)
+{
+  read_write_data (raw1394_start_read, handle, chunk_size, node, pointer,
+                   data, length, tag);
+}
+
+static gboolean
+raw_activity (GIOChannel   *channel,
+              GIOCondition  condition,
+	      gpointer      data)
+{
+  raw1394handle_t handle = data;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (handle != NULL, FALSE);
+
+  raw1394_loop_iterate (handle);
+
+  return TRUE;
+}
+
+static gint
+tag_handler (raw1394handle_t   handle,
+	     gulong            the_tag,
+	     raw1394_errcode_t errcode)
+{
+  SBP2Tag *tag = (SBP2Tag *) the_tag;
+
+  g_return_val_if_fail (handle != NULL, -1);
+  g_return_val_if_fail (tag != NULL, -1);
+  g_return_val_if_fail (tag->ref_count > 0, -1);
+
+  tag->ref_count--;
+
+  if (tag->ref_count == 0)
+    {
+      gboolean free_data = TRUE;
+
+      if (errcode != 0 && tag->process)
+	free_data = tag->process (tag->data);
+
+      if (free_data && tag->free)
+	tag->free (tag->data);
+
+      g_free (tag);
+    }
+
+  return 0;
+}
+
+static void
+read_write_data (ReadWriteFunc    read_write_func,
+		 raw1394handle_t  handle,
+		 gsize            chunk_size,
+		 nodeid_t         node,
+		 SBP2Pointer     *pointer,
+		 gpointer         data,
+		 gsize            length,
+		 SBP2Tag         *tag)
+{
+  nodeaddr_t  address;
+  guint8     *buffer;
+  gsize       left;
+
+  g_return_if_fail (handle != NULL);
+  g_return_if_fail (pointer != NULL);
+  g_return_if_fail (data != NULL);
+  g_return_if_fail (length > 0);
+  g_return_if_fail (tag != NULL);
+
+  if (chunk_size == 0)
+    chunk_size = length;
+
+  address = sbp2_pointer_to_1394_address (pointer);
+
+  buffer = data;
+  left = length;
+
+  while (left > chunk_size)
+    {
+      tag->ref_count++;
+
+      read_write_func (handle, node, address, chunk_size,
+		       (quadlet_t *) buffer, (gulong) tag);
+
+      buffer += chunk_size;
+      address += chunk_size;
+
+      left -= chunk_size;
+    }
+
+  tag->ref_count++;
+
+  read_write_func (handle, node, address, left,
+		   (quadlet_t *) buffer, (gulong) tag);
+}

Added: trunk/libsbp2/sbp2main.h
==============================================================================
--- trunk/libsbp2/sbp2main.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2main.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,65 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_MAIN_H__
+#define __SBP2_MAIN_H__
+
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2Tag SBP2Tag;
+
+typedef gboolean (*SBP2TagProcessFunc) (gpointer  data);
+typedef void     (*SBP2TagFreeFunc)    (gpointer  data);
+
+
+guint    sbp2_watch_handle (raw1394handle_t     handle,
+			    GMainContext       *context);
+
+SBP2Tag *sbp2_tag_new      (SBP2TagProcessFunc  process,
+			    SBP2TagFreeFunc     free,
+			    gpointer            data);
+
+void     sbp2_write_data   (raw1394handle_t     handle,
+			    gsize               chunk_size,
+			    nodeid_t            node,
+			    SBP2Pointer        *pointer,
+			    gpointer            data,
+			    gsize               length);
+
+void     sbp2_read_data    (raw1394handle_t     handle,
+                            gsize               chunk_size,
+			    nodeid_t            node,
+			    SBP2Pointer        *pointer,
+			    gpointer            data,
+			    gsize               length,
+			    SBP2Tag            *tag);
+
+
+#endif /* __SBP2_MAIN_H__ */

Added: trunk/libsbp2/sbp2manager.c
==============================================================================
--- trunk/libsbp2/sbp2manager.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2manager.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,423 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+#include <libraw1394/csr.h>
+
+#include "sbp2byteswap.h"
+#include "sbp2configrom.h"
+#include "sbp2csr.h"
+#include "sbp2manager.h"
+#include "sbp2main.h"
+#include "sbp2raw1394.h"
+
+
+enum
+{
+  FUNC_LOGIN_REQUEST        = 0x0,
+  FUNC_QUERY_LOGINS_REQUEST = 0x1,
+  FUNC_RECONNECT_REQUEST    = 0x3,
+  FUNC_SET_PASSWORD_REQUEST = 0x4,
+  FUNC_LOGOUT_REQUEST       = 0x7,
+  FUNC_ABORT_TASK_REQUEST   = 0xb,
+  FUNC_ABORT_TASK_SET       = 0xc,
+  FUNC_LOGICAL_UNIT_RESET   = 0xe,
+  FUNC_TARGET_RESET_REQUEST = 0xf
+};
+
+
+typedef struct raw1394_arm_request ARMRequest;
+typedef struct raw1394_arm_request_response ARMReqResp;
+
+typedef struct _SBP2ManagementORB SBP2ManagementORB;
+typedef struct _ORBData ORBData;
+
+struct _SBP2ManagementORB
+{
+  guint32     reserved1;
+  guint32     reserved2;
+  guint32     reserved3;
+  guint32     reserved4;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify    : 1;
+  guint       req_fmt   : 2;
+  guint       reserved5 : 9;
+  guint       function  : 4;
+  guint       reserved6 : 16;
+#else
+  guint       reserved6 : 16;
+  guint       function  : 4;
+  guint       reserved5 : 9;
+  guint       req_fmt   : 2;
+  guint       notify    : 1;
+#endif
+  guint32     reserved7;
+  SBP2Pointer status_FIFO;
+};
+
+struct _ORBData
+{
+  SBP2Manager       *manager;
+
+  nodeid_t           node;
+  SBP2Pointer        pointer;
+
+  SBP2ManagementORB  orb;
+};
+
+struct _SBP2Manager
+{
+  raw1394handle_t               handle;
+  guint                         id;
+
+  struct raw1394_arm_reqhandle  arm_management_agent;
+
+  SBP2ManagerFuncs             *funcs;
+  gpointer                      user_data;
+};
+
+
+static gint     management_agent  (raw1394handle_t    handle,
+                                   ARMReqResp        *arm_req_resp,
+                                   guint              requested_length,
+                                   gpointer           manager,
+                                   guint8             request_type);
+
+static void     fetch_orb         (SBP2Manager       *manager,
+                                   nodeid_t           node,
+				   SBP2Pointer       *pointer);
+static gboolean process_orb       (gpointer           data);
+
+static gboolean login             (SBP2ManagerAction *action);
+static gboolean query_logins      (SBP2ManagerAction *action);
+static gboolean reconnect         (SBP2ManagerAction *action);
+static gboolean logout            (SBP2ManagerAction *action);
+
+static gint     bus_reset_handler (raw1394handle_t    handle,
+                                   guint              generation);
+
+
+SBP2Manager *
+sbp2_manager_new (SBP2ManagerFuncs *funcs,
+                  GMainContext     *context,
+                  gint              port,
+                  gpointer          user_data)
+{
+  SBP2Manager     *manager = NULL;
+  raw1394handle_t  handle = NULL;
+  gint             ret;
+
+  handle = sbp2_raw1394_handle ();
+
+  if (!handle)
+    goto fail;
+
+  if (raw1394_set_port (handle, port) != 0)
+    goto fail;
+
+  if (!sbp2_setup_config_rom (handle))
+    goto fail;
+
+  manager = g_new (SBP2Manager, 1);
+
+  manager->arm_management_agent.arm_callback = management_agent;
+  manager->arm_management_agent.pcontext = manager;
+
+  ret = raw1394_arm_register (handle,
+                              CSR_REGISTER_BASE + SBP2_CSR_MANANGEMENT_AGENT,
+			      8, NULL,
+			      (gulong) &manager->arm_management_agent,
+			      RAW1394_ARM_READ | RAW1394_ARM_WRITE,
+			      RAW1394_ARM_WRITE,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  raw1394_set_userdata (handle, manager);
+  raw1394_set_bus_reset_handler (handle, bus_reset_handler);
+
+  manager->handle = handle;
+  manager->id = sbp2_watch_handle (handle, context);
+
+  manager->funcs = funcs;
+  manager->user_data = user_data;
+
+  return manager;
+
+fail:
+  g_free (manager);
+  raw1394_destroy_handle (handle);
+
+  return NULL;
+}
+
+void
+sbp2_manager_destroy (SBP2Manager *manager)
+{
+  g_return_if_fail (manager != NULL);
+
+  g_source_remove (manager->id);
+  
+  raw1394_destroy_handle (manager->handle);
+
+  g_free (manager);
+}
+
+void
+sbp2_manager_write_status (SBP2Manager *manager,
+                           nodeid_t     node,
+			   SBP2Pointer *pointer,
+			   SBP2Status  *status)
+{
+  gsize length;
+
+  g_return_if_fail (manager != NULL);
+  g_return_if_fail (pointer != NULL);
+  g_return_if_fail (status != NULL);
+
+  length = (status->len + 1) * 4;
+
+  sbp2_cpu_to_be32_data (status, length);
+  sbp2_write_data (manager->handle, 0, node, pointer, status, length);
+}
+
+void
+sbp2_manager_reset_bus (SBP2Manager *manager)
+{
+  g_return_if_fail (manager != NULL);
+
+  raw1394_reset_bus (manager->handle);
+}
+
+static gint
+management_agent (raw1394handle_t  handle,
+                  ARMReqResp      *arm_req_resp,
+		  guint            requested_length,
+		  gpointer         manager,
+		  guint8           request_type)
+{
+  ARMRequest  *request;
+  SBP2Pointer *pointer;
+
+  g_return_val_if_fail (manager != NULL, -1);
+
+  request = arm_req_resp->request;
+
+  pointer = (SBP2Pointer *) request->buffer;
+  sbp2_be32_to_cpu_data (pointer, sizeof (*pointer));
+
+  fetch_orb (manager, request->source_nodeid, pointer);
+
+  return 0;
+}
+
+static void
+fetch_orb (SBP2Manager *manager,
+           nodeid_t     node,
+	   SBP2Pointer *pointer)
+{
+  ORBData *orb_data;
+  SBP2Tag *tag;
+
+  g_return_if_fail (manager != NULL);
+  g_return_if_fail (pointer != NULL);
+
+  orb_data = g_new (ORBData, 1);
+
+  orb_data->manager = manager;
+
+  orb_data->node = node;
+  memcpy (&orb_data->pointer, pointer, sizeof (orb_data->pointer));
+
+  tag = sbp2_tag_new (process_orb, g_free, orb_data);
+
+  sbp2_read_data (manager->handle, 0, node, pointer,
+		  &orb_data->orb, sizeof (orb_data->orb),
+		  tag);
+}
+
+static gboolean
+process_orb (gpointer data)
+{
+  ORBData           *orb_data = data;
+  nodeid_t           node;
+  SBP2Pointer       *pointer;
+  SBP2ManagementORB *orb;
+  SBP2Manager       *manager;
+  SBP2Status        *status;
+  SBP2ManagerAction  action;
+  gboolean           ret;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+
+  manager = orb_data->manager;
+
+  node = orb_data->node;
+  pointer = &orb_data->pointer;
+
+  orb = &orb_data->orb;
+
+  sbp2_be32_to_cpu_data (orb, sizeof (*orb));
+
+  status = g_new0 (SBP2Status, 1);
+
+  sbp2_status_fill_pointer (status, pointer);
+
+  status->len = 1;
+  status->src = SBP2_STATUS_ORIGIN_NULL;
+  status->resp = SBP2_STATUS_RESP_REQUEST_COMPLETE;
+
+  action.manager   = manager;
+  action.node      = node;
+  action.pointer   = pointer;
+  action.status    = status;
+  action.orb       = orb;
+  action.user_data = manager->user_data;
+
+  switch (orb->function)
+    {
+    case FUNC_LOGIN_REQUEST:
+      ret = login (&action);
+      break;
+
+    case FUNC_QUERY_LOGINS_REQUEST:
+      ret = query_logins (&action);
+      break;
+
+    case FUNC_RECONNECT_REQUEST:
+      ret = reconnect (&action);
+      break;
+
+    case FUNC_LOGOUT_REQUEST:
+      ret = logout (&action);
+      break;
+
+    case FUNC_SET_PASSWORD_REQUEST:
+    case FUNC_ABORT_TASK_REQUEST:
+    case FUNC_ABORT_TASK_SET:
+    case FUNC_LOGICAL_UNIT_RESET:
+    case FUNC_TARGET_RESET_REQUEST:
+    default:
+      status->resp = SBP2_STATUS_RESP_ILLEGAL_REQUEST;
+      ret = TRUE;
+      break; 
+    }
+
+  if (ret)
+    sbp2_manager_write_status (manager, node, &orb->status_FIFO, status);
+  else
+    g_free (status);
+
+  return TRUE;
+}
+
+static gboolean
+login (SBP2ManagerAction *action)
+{
+  SBP2LoginORB      *orb = action->orb;
+  SBP2LoginResponse *resp;
+  SBP2Manager       *manager = action->manager;
+  gint               length;
+
+  resp = g_new0 (SBP2LoginResponse, 1);
+
+  if (!manager->funcs->login (action, orb, resp))
+    {
+      g_free (resp);
+      return FALSE;
+    }
+
+  length = resp->length;
+
+  sbp2_cpu_to_be32_data (resp, length);
+  sbp2_write_data (manager->handle, 0, action->node, &orb->login_resp,
+		   resp, length);
+
+  return TRUE;
+}
+
+static gboolean
+query_logins (SBP2ManagerAction *action)
+{
+  SBP2QueryLoginsORB      *orb = action->orb;
+  SBP2QueryLoginsResponse *resp;
+  SBP2Manager             *manager = action->manager;
+  gsize                    length;
+
+  resp = g_new0 (SBP2QueryLoginsResponse, 1);
+
+  if (!manager->funcs->query_logins (action, orb, resp))
+    {
+      g_free (resp);
+      return FALSE;
+    }
+
+  length = 4; /* TODO: use resp->length */
+
+  sbp2_cpu_to_be32_data (resp, length);
+  sbp2_write_data (manager->handle, 0, action->node, &orb->query_resp,
+		   resp, length);
+
+  return TRUE;
+}
+
+static gboolean
+reconnect (SBP2ManagerAction *action)
+{
+  SBP2ReconnectORB *orb = action->orb;
+  SBP2Manager      *manager = action->manager;
+
+  return manager->funcs->reconnect (action, orb);
+}
+
+static gboolean
+logout (SBP2ManagerAction *action)
+{
+  SBP2LogoutORB *orb = action->orb;
+  SBP2Manager   *manager = action->manager;
+
+  return manager->funcs->logout (action, orb);
+}
+
+static gint
+bus_reset_handler (raw1394handle_t handle,
+		   guint           generation)
+{
+  SBP2Manager *manager;
+
+  g_return_val_if_fail (handle != NULL, -1);
+
+  raw1394_update_generation (handle, generation);
+
+  manager = raw1394_get_userdata (handle);
+
+  g_return_val_if_fail (manager != NULL, -1);
+
+  manager->funcs->bus_reset (manager, manager->user_data);
+
+  return 0;
+}

Added: trunk/libsbp2/sbp2manager.h
==============================================================================
--- trunk/libsbp2/sbp2manager.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2manager.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,91 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_MANAGER_H__
+#define __SBP2_MANAGER_H__
+
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <libsbp2/sbp2login.h>
+#include <libsbp2/sbp2logout.h>
+#include <libsbp2/sbp2querylogins.h>
+#include <libsbp2/sbp2reconnect.h>
+#include <libsbp2/sbp2status.h>
+
+
+typedef struct _SBP2Manager SBP2Manager;
+typedef struct _SBP2ManagerAction SBP2ManagerAction;
+typedef struct _SBP2ManagerFuncs SBP2ManagerFuncs;
+
+struct _SBP2ManagerAction
+{
+  SBP2Manager  *manager;
+
+  nodeid_t      node;
+  SBP2Pointer  *pointer;
+
+  SBP2Status   *status;
+
+  gpointer      orb;
+  gpointer      user_data;
+};
+
+struct _SBP2ManagerFuncs
+{
+  void     (*bus_reset)    (SBP2Manager             *manager,
+                            gpointer                 user_data);
+
+  gboolean (*login)        (SBP2ManagerAction       *action,
+                            SBP2LoginORB            *orb,
+                            SBP2LoginResponse       *resp);
+  gboolean (*query_logins) (SBP2ManagerAction       *action,
+                            SBP2QueryLoginsORB      *orb,
+                            SBP2QueryLoginsResponse *resp);
+  gboolean (*reconnect)    (SBP2ManagerAction       *action,
+                            SBP2ReconnectORB        *orb);
+  gboolean (*logout)       (SBP2ManagerAction       *action,
+                            SBP2LogoutORB           *orb);
+
+  /* TODO: task_management */
+};
+
+
+SBP2Manager *sbp2_manager_new          (SBP2ManagerFuncs *funcs,
+					GMainContext     *context,
+					gint              port,
+					gpointer          user_data);
+
+void         sbp2_manager_destroy      (SBP2Manager      *manager);
+
+void         sbp2_manager_write_status (SBP2Manager      *manager,
+                                        nodeid_t          node,
+					SBP2Pointer      *pointer,
+					SBP2Status       *status);
+
+void         sbp2_manager_reset_bus    (SBP2Manager      *manager);
+
+
+#endif /* __SBP2_MANAGER_H__ */

Added: trunk/libsbp2/sbp2pagetable.h
==============================================================================
--- trunk/libsbp2/sbp2pagetable.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2pagetable.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,63 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_PAGE_TABLE_H__
+#define __SBP2_PAGE_TABLE_H__
+
+
+#include <glib.h>
+
+
+typedef struct _SBP2Page SBP2Page;
+typedef struct _SBP2PageNormalized SBP2PageNormalized;
+
+struct _SBP2Page
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint   segment_length  : 16;
+  guint   segment_base_hi : 16;
+#else
+  guint   segment_base_hi : 16;
+  guint   segment_length  : 16;
+#endif
+  guint32 segment_base_lo;
+};
+
+struct _SBP2PageNormalized
+{
+  /* TODO */
+  guint32 unimplmented1;
+  guint32 unimplmented2;
+};
+
+
+static inline void
+sbp2_pointer_from_page (SBP2Pointer *p,
+                        SBP2Page    *page)
+{
+  p->addr_lo = page->segment_base_lo;
+  p->addr_hi = page->segment_base_hi;
+}
+
+
+#endif /* __SBP2_PAGE_TABLE_H__ */

Added: trunk/libsbp2/sbp2pointer.h
==============================================================================
--- trunk/libsbp2/sbp2pointer.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2pointer.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,73 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_POINTER_H__
+#define __SBP2_POINTER_H__
+
+
+#include <glib.h>
+
+
+typedef struct _SBP2Pointer SBP2Pointer;
+
+struct _SBP2Pointer
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint   node    : 16;
+  guint   addr_hi : 16;
+#else
+  guint   addr_hi : 16;
+  guint   node    : 16;
+#endif
+  guint32 addr_lo;
+};
+
+
+static inline guint64
+sbp2_pointer_to_1394_address (SBP2Pointer *p)
+{
+  guint64 address;
+
+  address = p->addr_lo;
+  address |= (guint64) p->addr_hi << 32;
+
+  return address;
+}
+
+static inline void
+sbp2_pointer_from_1394_address (SBP2Pointer *p,
+                                guint64      address)
+{
+  p->addr_lo = address & 0xffffffff;
+  p->addr_hi |= (address >> 32) & 0xffff;
+}
+
+
+static inline gboolean
+sbp2_pointer_is_null_orb (SBP2Pointer *p)
+{
+  return p->node & 0x8000;
+}
+
+
+#endif /* __SBP2_POINTER_H__ */

Added: trunk/libsbp2/sbp2querylogins.h
==============================================================================
--- trunk/libsbp2/sbp2querylogins.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2querylogins.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,75 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_QUERY_LOGINS_H__
+#define __SBP2_QUERY_LOGINS_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2QueryLoginsORB SBP2QueryLoginsORB;
+typedef struct _SBP2QueryLoginsResponse SBP2QueryLoginsResponse;
+
+struct _SBP2QueryLoginsORB
+{
+  guint32     reserved1;
+  guint32     reserved2;
+  SBP2Pointer query_resp;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify       : 1;
+  guint       req_fmt      : 2;
+  guint       reserved3    : 9;
+  guint       function     : 4;
+  guint       lun          : 16;
+  guint       reserved4    : 16;
+  guint       resp_length  : 16;
+#else
+  guint       lun          : 16;
+  guint       function     : 4;
+  guint       reserved3    : 9;
+  guint       req_fmt      : 2;
+  guint       notify       : 1;
+  guint       resp_length  : 16;
+  guint       reserved4    : 16;
+#endif
+  SBP2Pointer status_FIFO;
+};
+
+struct _SBP2QueryLoginsResponse
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint length     : 16;
+  guint max_logins : 16;
+#else
+  guint max_logins : 16;
+  guint length     : 16;
+#endif
+
+  /* TODO: Logged in initiator listing here, ignored for now */
+};
+
+
+#endif /* __SBP2_QUERY_LOGINS_H__ */

Added: trunk/libsbp2/sbp2raw1394.c
==============================================================================
--- trunk/libsbp2/sbp2raw1394.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2raw1394.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,89 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <errno.h>
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include "sbp2raw1394.h"
+
+
+static const gchar *errmsg_not_compatible =
+"This libraw1394 does not work with your version of Linux. You need a "
+"different version that matches your kernel (see kernel help text for the "
+"raw1394 option to find out which is the correct version).";
+
+static const gchar *errmsg_not_loaded =
+"This probably means that you don't have raw1394 support in the kernel or "
+"that you haven't loaded the raw1394 module.";
+
+
+raw1394handle_t
+sbp2_raw1394_handle (void)
+{
+  raw1394handle_t handle;
+
+  handle = raw1394_new_handle ();
+
+  if (!handle)
+    {
+      if (!errno)
+	{
+	  g_critical (errmsg_not_compatible);
+	}
+      else
+	{
+	  g_critical ("Couldn't get handle");
+	  g_critical (errmsg_not_loaded);
+	}
+    }
+
+  return handle;
+}
+
+gint
+sbp2_raw1394_get_num_ports (void)
+{
+  raw1394handle_t handle;
+  gint            num_ports;
+
+  handle = sbp2_raw1394_handle ();
+
+  if (!handle)
+    return 0;
+
+  num_ports = raw1394_get_port_info (handle, NULL, 0);
+
+  raw1394_destroy_handle (handle);
+
+  return num_ports;
+}
+
+guint64
+sbp2_raw1394_get_guid (raw1394handle_t handle,
+                       nodeid_t        node)
+{
+  return 0;
+}

Added: trunk/libsbp2/sbp2raw1394.h
==============================================================================
--- trunk/libsbp2/sbp2raw1394.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2raw1394.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,41 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_RAW1394_H__
+#define __SBP2_RAW1394_H__
+
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+
+raw1394handle_t sbp2_raw1394_handle        (void);
+
+gint            sbp2_raw1394_get_num_ports (void);
+
+guint64         sbp2_raw1394_get_guid      (raw1394handle_t handle,
+                                            nodeid_t        node);
+
+
+#endif /* __SBP2_RAW1394_H__ */

Added: trunk/libsbp2/sbp2reconnect.h
==============================================================================
--- trunk/libsbp2/sbp2reconnect.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2reconnect.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,59 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_RECONNECT_H__
+#define __SBP2_RECONNECT_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2ReconnectORB SBP2ReconnectORB;
+
+struct _SBP2ReconnectORB
+{
+  guint32     reserved1;
+  guint32     reserved2;
+  guint32     reserved3;
+  guint32     reserved4;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify    : 1;
+  guint       req_fmt   : 2;
+  guint       reserved5 : 9;
+  guint       function  : 4;
+  guint       login_ID  : 16;
+#else
+  guint       login_ID  : 16;
+  guint       function  : 4;
+  guint       reserved5 : 9;
+  guint       req_fmt   : 2;
+  guint       notify    : 1;
+#endif
+  guint32     reserved6;
+  SBP2Pointer status_FIFO;
+};
+
+
+#endif /* __SBP2_RECONNECT_H__ */

Added: trunk/libsbp2/sbp2scsicommand.h
==============================================================================
--- trunk/libsbp2/sbp2scsicommand.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2scsicommand.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,64 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_SCSI_COMMAND_H__
+#define __SBP2_SCSI_COMMAND_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef struct _SBP2ScsiCommandORB SBP2ScsiCommandORB;
+
+struct _SBP2ScsiCommandORB
+{
+  SBP2Pointer next_ORB;
+  SBP2Pointer data_desc;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint       notify         : 1;
+  guint       req_fmt        : 2;
+  guint       reserved       : 1;
+  guint       direction      : 1;
+  guint       speed          : 3;
+  guint       max_payload    : 4;
+  guint       has_page_table : 1;
+  guint       page_size      : 3;
+  guint       data_size      : 16;
+#else
+  guint       data_size      : 16;
+  guint       page_size      : 3;
+  guint       has_page_table : 1;
+  guint       max_payload    : 4;
+  guint       speed          : 3;
+  guint       direction      : 1;
+  guint       reserved       : 1;
+  guint       req_fmt        : 2;
+  guint       notify         : 1;
+#endif
+  guint8      cdb[12];
+};
+
+
+#endif /* __SBP2_SCSI_COMMAND_H__ */

Added: trunk/libsbp2/sbp2scsistatus.h
==============================================================================
--- trunk/libsbp2/sbp2scsistatus.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2scsistatus.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,81 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_SCSI_STATUS_H__
+#define __SBP2_SCSI_STATUS_H__
+
+
+#include <glib.h>
+
+
+typedef struct _SBP2ScsiStatus SBP2ScsiStatus;
+
+struct _SBP2ScsiStatus
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint   src             : 2;
+  guint   resp            : 2;
+  guint   dead            : 1;
+  guint   len             : 3;
+  guint   sbp_status      : 8;
+  guint   ORB_hi          : 16;
+  guint32 ORB_lo;
+  guint   sfmt            : 2;
+  guint   status          : 6;
+  guint   valid           : 1;
+  guint   mark            : 1;
+  guint   eom             : 1;
+  guint   ili             : 1;
+  guint   sense_key       : 4;
+  guint   sense_code      : 8;
+  guint   sense_qualifier : 8;
+  guint32 information;
+  guint32 cdb_dependent;
+  guint   fru             : 8;
+  guint   sense_key_dep   : 12;
+#else
+  guint   ORB_hi          : 16;
+  guint   sbp_status      : 8;
+  guint   len             : 3;
+  guint   dead            : 1;
+  guint   resp            : 2;
+  guint   src             : 2;
+  guint32 ORB_lo;
+  guint   sense_qualifier : 8;
+  guint   sense_code      : 8;
+  guint   sense_key       : 4;
+  guint   ili             : 1;
+  guint   eom             : 1;
+  guint   mark            : 1;
+  guint   valid           : 1;
+  guint   status          : 6;
+  guint   sfmt            : 2;
+  guint32 information;
+  guint32 cdb_dependent;
+  guint   sense_key_dep   : 12;
+  guint   fru             : 8;
+#endif
+};
+
+
+#endif /* __SBP2_SCSI_STATUS_H__ */

Added: trunk/libsbp2/sbp2status.h
==============================================================================
--- trunk/libsbp2/sbp2status.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2status.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,102 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_STATUS_H__
+#define __SBP2_STATUS_H__
+
+
+#include <glib.h>
+
+#include <libsbp2/sbp2pointer.h>
+
+
+typedef enum
+{
+  SBP2_STATUS_ORIGIN_NON_NULL    = 0x0,
+  SBP2_STATUS_ORIGIN_NULL        = 0x1,
+  SBP2_STATUS_ORIGIN_UNSOLICITED = 0x2,
+  SBP2_STATUS_ORIGIN_RESERVED    = 0x3
+} SBP2StatusOrigin;
+
+typedef enum
+{
+  SBP2_STATUS_RESP_REQUEST_COMPLETE  = 0x0,
+  SBP2_STATUS_RESP_TRANSPORT_FAILURE = 0x1,
+  SBP2_STATUS_RESP_ILLEGAL_REQUEST   = 0x2,
+  SBP2_STATUS_RESP_VENDOR_DEPENDENT  = 0x3
+} SBP2StatusResponse;
+
+typedef enum
+{
+  SBP2_STATUS_REQUEST_NO_ADDITIONAL_INFO      = 0x0,
+  SBP2_STATUS_REQUEST_REQ_TYPE_NOT_SUPPORTED  = 0x1,
+  SBP2_STATUS_REQUEST_SPEED_NOT_SUPPORTED     = 0x2,
+  SBP2_STATUS_REQUEST_PAGE_SIZE_NOT_SUPPORTED = 0x3,
+  SBP2_STATUS_REQUEST_ACCESS_DENIED           = 0x4,
+  SBP2_STATUS_REQUEST_LU_NOT_SUPPORTED        = 0x5,
+  SBP2_STATUS_REQUEST_MAX_PAYLOAD_TOO_SMALL   = 0x6,
+  SBP2_STATUS_REQUEST_RESOURCES_UNAVAILABLE   = 0x8,
+  SBP2_STATUS_REQUEST_FUNCTION_REJECTED       = 0x9,
+  SBP2_STATUS_REQUEST_LOGIN_ID_NOT_RECOGNIZED = 0xa,
+  SBP2_STATUS_REQUEST_DUMMY_ORB_COMPLETED     = 0xb,
+  SBP2_STATUS_REQUEST_REQUEST_ABORTED         = 0xc,
+  SBP2_STATUS_REQUEST_UNSPECIFIED_ERROR       = 0xff
+} SBP2StatusRequest;
+
+/* TODO: transport failure error codes */
+
+
+typedef struct _SBP2Status SBP2Status;
+
+struct _SBP2Status
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint   src        : 2;
+  guint   resp       : 2;
+  guint   dead       : 1;
+  guint   len        : 3;
+  guint   sbp_status : 8;
+  guint   ORB_hi     : 16;
+#else
+  guint   ORB_hi     : 16;
+  guint   sbp_status : 8;
+  guint   len        : 3;
+  guint   dead       : 1;
+  guint   resp       : 2;
+  guint   src        : 2;
+#endif
+  guint32 ORB_lo;
+  guint8  sense[24];
+};
+
+
+static inline void
+sbp2_status_fill_pointer (SBP2Status  *s,
+                          SBP2Pointer *p)
+{
+  s->ORB_hi = p->addr_hi;
+  s->ORB_lo = p->addr_lo;
+}
+
+
+#endif /* __SBP2_STATUS_H__ */

Added: trunk/libsbp2/sbp2worker.c
==============================================================================
--- trunk/libsbp2/sbp2worker.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2worker.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,832 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+ 
+#include <libraw1394/raw1394.h>
+#include <libraw1394/csr.h>
+
+#include <librbc/rbcdisk.h>
+#include <librbc/rbccommand.h>
+
+#include "sbp2byteswap.h"
+#include "sbp2constants.h"
+#include "sbp2csr.h"
+#include "sbp2main.h"
+#include "sbp2pagetable.h"
+#include "sbp2raw1394.h"
+#include "sbp2scsicommand.h"
+#include "sbp2status.h"
+#include "sbp2worker.h"
+
+
+enum
+{
+  CMD_AGENT_STATE               = 0x00,
+  CMD_AGENT_RESET               = 0x04,
+  CMD_ORB_POINTER               = 0x08,
+  CMD_DOORBELL                  = 0x10,
+  CMD_UNSOLICITED_STATUS_ENABLE = 0x14
+};
+
+typedef enum
+{
+  STATE_RESET     = 0x0,
+  STATE_ACTIVE    = 0x1,
+  STATE_SUSPENDED = 0x2,
+  STATE_DEAD      = 0x3
+} State;
+
+
+typedef struct raw1394_arm_request ARMRequest;
+typedef struct raw1394_arm_request_response ARMReqResp;
+
+typedef struct _ORBData ORBData;
+
+struct _ORBData
+{
+  SBP2Worker         *worker;
+
+  gsize               chunk_size;
+
+  nodeid_t            node;
+  SBP2Pointer         pointer;
+
+  SBP2ScsiCommandORB  orb;
+  SBP2Status         *status;
+
+  RBCBuffer          *buffer;
+
+  SBP2Pointer        *page_pointers;
+};
+
+struct _SBP2Worker
+{
+  raw1394handle_t               handle;
+  guint                         id;
+
+  RBCDisk                      *disk;
+
+  gboolean                      unattached;
+
+  State                         state;
+  gboolean                      doorbell;
+
+  guint                         login_ID;
+
+  SBP2Pointer                  *pointer;
+
+  nodeaddr_t                    csr_address;
+  SBP2Pointer                   status_FIFO;
+
+  struct raw1394_arm_reqhandle  arm_agent_state;
+  struct raw1394_arm_reqhandle  arm_agent_reset;
+  struct raw1394_arm_reqhandle  arm_orb_pointer;
+  struct raw1394_arm_reqhandle  arm_doorbell;
+  struct raw1394_arm_reqhandle  arm_unsolicited_status_enable;
+};
+
+
+static gint     agent_reset               (raw1394handle_t  handle,
+                                           ARMReqResp      *arm_req_resp,
+					   guint            requested_length,
+					   gpointer         pcontext,
+					   guint8           request_type);
+static gint     orb_pointer               (raw1394handle_t  handle,
+                                           ARMReqResp      *arm_req_resp,
+					   guint            requested_length,
+					   gpointer         pcontext,
+					   guint8           request_type);
+static gint     doorbell                  (raw1394handle_t  handle,
+                                           ARMReqResp      *arm_req_resp,
+					   guint            requested_length,
+					   gpointer         pcontext,
+					   guint8           request_type);
+static gint     unsolicited_status_enable (raw1394handle_t  handle,
+                                           ARMReqResp      *arm_req_resp,
+					   guint            requested_length,
+					   gpointer         pcontext,
+					   guint8           request_type);
+
+static void     fetch_orb                 (SBP2Worker      *worker,
+                                           nodeid_t         node,
+					   SBP2Pointer     *pointer);
+static gboolean process_orb               (gpointer         data);
+
+static gboolean fetch_data                (gpointer         data);
+static gboolean process_data              (gpointer         data);
+
+static gboolean fetch_page_table          (gpointer         data);
+static gboolean process_page_table        (gpointer         data);
+
+static gboolean fetch_page_table_data     (gpointer         data);
+
+static void     complete_orb              (ORBData         *orb_data);
+
+static void     write_orb_data_buffer     (ORBData         *orb_data);
+
+static void     free_orb_data             (gpointer         data);
+
+static gint     bus_reset_handler         (raw1394handle_t  handle,
+                                           guint            generation);
+
+
+SBP2Worker *
+sbp2_worker_new (RBCDisk      *disk,
+                 GMainContext *context,
+                 gint          port,
+                 guint         login_ID)
+{
+  SBP2Worker      *worker = NULL;
+  raw1394handle_t  handle;
+  nodeaddr_t       csr_address;
+  gint             ret;
+
+  handle = sbp2_raw1394_handle ();
+
+  if (!handle)
+    return NULL;
+
+  if (raw1394_set_port (handle, port) != 0)
+    goto fail;
+
+  csr_address = CSR_REGISTER_BASE + SBP2_CSR_COMMAND_BLOCK_AGENT_BASE +
+                (SBP2_CSR_COMMAND_BLOCK_AGENT_SIZE * login_ID);
+
+  worker = g_new (SBP2Worker, 1);
+
+  /* TODO: Way to update the register */
+  ret = raw1394_arm_register (handle,
+                              csr_address + CMD_AGENT_STATE,
+			      4, NULL,
+                              0,
+			      RAW1394_ARM_READ,
+			      0,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  worker->arm_agent_reset.arm_callback = agent_reset;
+  worker->arm_agent_reset.pcontext = worker;
+
+  ret = raw1394_arm_register (handle,
+                              csr_address + CMD_AGENT_RESET,
+			      4, NULL,
+                              (gulong) &worker->arm_agent_reset,
+			      RAW1394_ARM_WRITE,
+			      RAW1394_ARM_WRITE,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  worker->arm_orb_pointer.arm_callback = orb_pointer;
+  worker->arm_orb_pointer.pcontext = worker;
+
+  ret = raw1394_arm_register (handle,
+                              csr_address + CMD_ORB_POINTER,
+			      8, NULL,
+                              (gulong) &worker->arm_orb_pointer,
+			      RAW1394_ARM_READ | RAW1394_ARM_WRITE,
+			      RAW1394_ARM_WRITE,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  worker->arm_doorbell.arm_callback = doorbell;
+  worker->arm_doorbell.pcontext = worker;
+
+  ret = raw1394_arm_register (handle,
+                              csr_address + CMD_DOORBELL,
+			      4, NULL,
+                              (gulong) &worker->arm_doorbell,
+			      RAW1394_ARM_WRITE,
+			      RAW1394_ARM_WRITE,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  worker->arm_unsolicited_status_enable.arm_callback = unsolicited_status_enable;
+  worker->arm_unsolicited_status_enable.pcontext = worker;
+
+  ret = raw1394_arm_register (handle,
+                              csr_address + CMD_UNSOLICITED_STATUS_ENABLE,
+			      4, NULL,
+                              (gulong) &worker->arm_unsolicited_status_enable,
+			      RAW1394_ARM_WRITE,
+			      RAW1394_ARM_WRITE,
+			      0);
+  if (ret < 0)
+    goto fail;
+
+  raw1394_set_userdata (handle, worker);
+  raw1394_set_bus_reset_handler (handle, bus_reset_handler);
+
+  worker->disk = disk;
+
+  worker->unattached = TRUE;
+
+  worker->state = STATE_RESET;
+
+  worker->csr_address = csr_address;
+
+  worker->handle = handle;
+  worker->id = sbp2_watch_handle (handle, context);
+
+  return worker;
+
+fail:
+  g_free (worker);
+  raw1394_destroy_handle (handle);
+
+ return NULL; 
+}
+
+void
+sbp2_worker_destroy (SBP2Worker *worker)
+{
+  g_source_remove (worker->id);
+
+  raw1394_destroy_handle (worker->handle);
+
+  g_free (worker);
+}
+
+gboolean
+sbp2_worker_login (SBP2Worker   *worker,
+                   nodeid_t      node,
+		   SBP2Pointer  *pointer,
+                   SBP2LoginORB *orb)
+{
+  SBP2LoginResponse *resp;
+  SBP2Status        *status;
+  guint              length;
+
+  memcpy (&worker->status_FIFO, &orb->status_FIFO,
+          sizeof (worker->status_FIFO));
+
+  resp = g_new (SBP2LoginResponse, 1);
+
+  if (orb->resp_length >= 16)
+    length = 16;
+  else
+    length = 12;
+
+  resp->length = length;
+
+  resp->login_ID = worker->login_ID;
+
+  resp->reconnect_hold = SBP2_RECONNECT_HOLD;
+
+  resp->command_block_agent.node = raw1394_get_local_id (worker->handle);
+
+  sbp2_pointer_from_1394_address (&resp->command_block_agent,
+                                  worker->csr_address);
+
+  sbp2_cpu_to_be32_data (resp, length);
+  sbp2_write_data (worker->handle, 0, node, &orb->login_resp, resp, length);
+
+  status = g_new0 (SBP2Status, 1);
+
+  sbp2_status_fill_pointer (status, pointer);
+
+  status->len = 1;
+  status->src = SBP2_STATUS_ORIGIN_NULL;
+  status->resp = SBP2_STATUS_RESP_REQUEST_COMPLETE;
+
+  sbp2_cpu_to_be32_data (status, 8);
+  sbp2_write_data (worker->handle, 0, node, &orb->status_FIFO, status, 8);
+
+  worker->unattached = FALSE;
+
+  return TRUE;
+}
+
+gboolean
+sbp2_worker_reconnect (SBP2Worker *worker,
+                       nodeid_t    node)
+{
+  g_return_val_if_fail (worker != NULL, FALSE);
+
+  worker->unattached = FALSE;
+
+  return TRUE;
+}
+
+static gint
+agent_reset (raw1394handle_t  handle,
+             ARMReqResp      *arm_req_resp,
+	     guint            requested_length,
+	     gpointer         pcontext,
+	     guint8           request_type)
+{
+  SBP2Worker *worker = pcontext;
+
+  g_return_val_if_fail (worker != NULL, -1);
+
+  if (worker->unattached)
+    return 0;
+ 
+  worker->state = STATE_RESET;
+  worker->doorbell = 0;
+
+  return 0;
+}
+
+static gint
+orb_pointer (raw1394handle_t  handle,
+             ARMReqResp      *arm_req_resp,
+	     guint            requested_length,
+	     gpointer         pcontext,
+	     guint8           request_type)
+{
+  ARMRequest  *request;
+  SBP2Worker  *worker = pcontext;
+  SBP2Pointer *pointer;
+ 
+  g_return_val_if_fail (worker != NULL, -1);
+
+  if (worker->state == STATE_DEAD)
+    return 0;
+
+  request = arm_req_resp->request;
+
+  worker->state = STATE_ACTIVE;
+  worker->doorbell = 0;
+ 
+  pointer = (SBP2Pointer *) request->buffer;
+  sbp2_be32_to_cpu_data (pointer, sizeof (*pointer));
+
+  fetch_orb (worker, request->source_nodeid, pointer);
+
+  return 0;
+}
+
+static gint
+doorbell (raw1394handle_t  handle,
+          ARMReqResp      *arm_req_resp,
+	  guint            requested_length,
+	  gpointer         pcontext,
+	  guint8           request_type)
+{
+  ARMRequest *request;
+  SBP2Worker *worker = pcontext;
+
+  g_return_val_if_fail (worker != NULL, -1);
+ 
+  if (worker->state != STATE_SUSPENDED)
+    return 0;
+
+  request = arm_req_resp->request;
+
+  worker->state = STATE_ACTIVE;
+
+  fetch_orb (worker, request->source_nodeid, worker->pointer);
+
+  return 0;
+}
+
+
+static gint
+unsolicited_status_enable (raw1394handle_t  handle,
+			   ARMReqResp      *arm_req_resp,
+			   guint            requested_length,
+			   gpointer         pcontext,
+			   guint8           request_type)
+{
+  /* TODO */
+  return 0;
+}
+
+static void
+fetch_orb (SBP2Worker  *worker,
+           nodeid_t     node,
+	   SBP2Pointer *pointer)
+{
+  ORBData *orb_data;
+  SBP2Tag *tag;
+
+  g_return_if_fail (worker != NULL);
+  g_return_if_fail (pointer != NULL);
+
+  orb_data = g_new0 (ORBData, 1);
+
+  orb_data->worker = worker;
+
+  orb_data->node = node;
+  memcpy (&orb_data->pointer, pointer, sizeof (orb_data->pointer));
+
+  tag = sbp2_tag_new (process_orb, free_orb_data, orb_data);
+
+  sbp2_read_data (worker->handle, 0, node, pointer,
+                  &orb_data->orb, sizeof (orb_data->orb),
+		  tag);
+}
+
+static gboolean
+process_orb (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  nodeid_t            node;
+  SBP2Pointer        *pointer;
+  SBP2ScsiCommandORB *orb;
+  SBP2Status         *status;
+  RBCBuffer          *buffer;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+
+  worker = orb_data->worker;
+
+  node = orb_data->node;
+  pointer = &orb_data->pointer;
+  orb = &orb_data->orb;
+
+  sbp2_be32_to_cpu_data (orb, 20);
+
+  orb_data->chunk_size = 1 << (orb->max_payload + 2);
+
+  orb_data->status = status = g_new0 (SBP2Status, 1);
+
+  sbp2_status_fill_pointer (status, pointer);
+
+  status->len = 1;
+  status->resp = SBP2_STATUS_RESP_REQUEST_COMPLETE;
+
+  if (orb->req_fmt == 0x0)
+    {
+      if (orb->has_page_table)
+	{
+	  if (orb->page_size == 0)
+	    return fetch_page_table (orb_data);
+	  else
+	    status->sbp_status = SBP2_STATUS_REQUEST_PAGE_SIZE_NOT_SUPPORTED;
+	}
+      else
+	{
+	  if (orb->data_size != 0)
+	    {
+	      if (!orb->direction)
+		return fetch_data (orb_data);
+	      else
+		buffer = rbc_buffer_simple_new (orb->data_size);
+	    }
+	  else
+	    buffer = rbc_buffer_empty_new ();
+
+	  orb_data->buffer = buffer;
+
+	  if (rbc_disk_command (worker->disk, orb->cdb, buffer, status->sense))
+	    write_orb_data_buffer (orb_data);
+	  else
+	    status->len = 7;
+	}
+    }
+  else
+    status->sbp_status = SBP2_STATUS_REQUEST_DUMMY_ORB_COMPLETED;
+
+  complete_orb (orb_data);
+
+  return TRUE;
+}
+
+static gboolean
+fetch_data (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  SBP2ScsiCommandORB *orb;
+  RBCBuffer          *buffer;
+  gsize               chunk_size;
+  SBP2Tag            *tag;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+
+  worker = orb_data->worker;
+
+  orb = &orb_data->orb;
+
+  chunk_size = orb_data->chunk_size;
+
+  orb_data->buffer = buffer = rbc_buffer_simple_new (orb->data_size);
+
+  tag = sbp2_tag_new (process_data, free_orb_data, data);
+
+  sbp2_read_data (worker->handle, chunk_size, orb_data->node, &orb->data_desc,
+                  buffer->data, buffer->length,
+		  tag);
+
+  return FALSE;
+}
+
+static gboolean
+process_data (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  SBP2ScsiCommandORB *orb;
+  RBCBuffer          *buffer;
+  SBP2Status         *status;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+  g_return_val_if_fail (orb_data->status != NULL, FALSE);
+
+  worker = orb_data->worker;
+
+  orb = &orb_data->orb;
+  status = orb_data->status;
+
+  buffer = orb_data->buffer;
+
+  if (!rbc_disk_command (worker->disk, orb->cdb, buffer, status->sense))
+    status->len = 7;
+
+  complete_orb (orb_data);
+
+  return TRUE;
+}
+
+static gboolean
+fetch_page_table (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  SBP2ScsiCommandORB *orb;
+  gsize               chunk_size;
+  gsize               length;
+  RBCBuffer          *buffer;
+  SBP2Tag            *tag;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+
+  worker = orb_data->worker;
+
+  orb = &orb_data->orb;
+
+  chunk_size = orb_data->chunk_size;
+
+  length = sizeof (SBP2Page) * orb->data_size;
+
+  orb_data->buffer = buffer = rbc_buffer_simple_new (length);
+
+  tag = sbp2_tag_new (process_page_table, free_orb_data, data);
+
+  sbp2_read_data (worker->handle, chunk_size, orb_data->node, &orb->data_desc,
+                  buffer->data, length,
+		  tag);
+
+  return FALSE;
+}
+
+static gboolean
+process_page_table (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  SBP2ScsiCommandORB *orb;
+  gsize               data_size;
+  RBCBuffer          *buffer;
+  SBP2Page           *ext_page;
+  RBCPage            *page;
+  SBP2Pointer        *page_pointer;
+  gsize               i;
+  SBP2Status         *status;
+
+  g_return_val_if_fail (orb_data != NULL, TRUE);
+
+  worker = orb_data->worker;
+
+  orb = &orb_data->orb;
+  data_size = orb->data_size;
+
+  ext_page = orb_data->buffer->data;
+  sbp2_be32_to_cpu_data (ext_page, orb_data->buffer->length);
+
+  buffer = rbc_buffer_page_table_new (data_size);
+  page = buffer->data;
+
+  orb_data->page_pointers = page_pointer = g_new (SBP2Pointer, data_size);
+
+  for (i = data_size; i > 0; i--, page++, page_pointer++, ext_page++)
+    {
+      page->length = ext_page->segment_length;
+      page->data = g_malloc (page->length);
+
+      sbp2_pointer_from_page (page_pointer, ext_page);
+    }
+
+  rbc_buffer_destroy (orb_data->buffer);
+
+  orb_data->buffer = buffer;
+
+  if (orb->direction)
+    {
+      status = orb_data->status;
+
+      if (rbc_disk_command (worker->disk, orb->cdb, buffer, status->sense))
+	write_orb_data_buffer (orb_data);
+      else
+	status->len = 7;
+
+      complete_orb (orb_data);
+
+      return TRUE;
+    }
+
+  return fetch_page_table_data (orb_data);
+}
+
+static gboolean
+fetch_page_table_data (gpointer data)
+{
+  ORBData            *orb_data = data;
+  SBP2Worker         *worker;
+  raw1394handle_t     handle;
+  nodeid_t            node;
+  SBP2ScsiCommandORB *orb;
+  gsize               chunk_size;
+  RBCBuffer          *buffer;
+  RBCPage            *page;
+  SBP2Pointer        *page_pointer;
+  gsize               i;
+  SBP2Tag            *tag;
+
+  g_return_val_if_fail (orb_data != NULL, FALSE);
+
+  worker = orb_data->worker;
+  handle = worker->handle;
+
+  node = orb_data->node;
+  orb = &orb_data->orb;
+
+  chunk_size = orb_data->chunk_size;
+
+  buffer = orb_data->buffer;
+
+  page = buffer->data;
+  page_pointer = orb_data->page_pointers;
+
+  tag = sbp2_tag_new (process_data, free_orb_data, data);
+
+  for (i = buffer->length; i > 0; i--, page++, page_pointer++)
+    sbp2_read_data (handle, chunk_size, node, page_pointer,
+		    page->data, page->length,
+		    tag);
+
+  return FALSE;
+}
+
+static void
+complete_orb (ORBData *orb_data)
+{
+  SBP2Worker         *worker;
+  nodeid_t            node;
+  SBP2ScsiCommandORB *orb;
+  SBP2Status         *status;
+  gsize               length;
+
+  g_return_if_fail (orb_data != NULL);
+  g_return_if_fail (orb_data->status != NULL);
+
+  worker = orb_data->worker;
+
+  node = orb_data->node;
+  orb = &orb_data->orb;
+  status = orb_data->status;
+
+  length = (status->len + 1) * 4;
+
+  if (!sbp2_pointer_is_null_orb (&orb->next_ORB))
+    {
+      status->src = SBP2_STATUS_ORIGIN_NON_NULL;
+      fetch_orb (worker, node, &orb->next_ORB);
+    }
+  else
+    status->src = SBP2_STATUS_ORIGIN_NULL;
+
+  sbp2_cpu_to_be32_data (status, length);
+  sbp2_write_data (worker->handle, 0, node, &worker->status_FIFO,
+                   status, length);
+
+  orb_data->status = NULL;
+}
+
+static void
+write_orb_data_buffer (ORBData *orb_data)
+{
+  SBP2Worker         *worker;
+  raw1394handle_t     handle;
+  nodeid_t            node;
+  SBP2ScsiCommandORB *orb;
+  gsize               chunk_size;
+  RBCBuffer          *buffer;
+  SBP2Pointer        *page_pointer;
+  RBCPage            *page;
+  gsize               i;
+
+  g_return_if_fail (orb_data != NULL);
+  g_return_if_fail (orb_data->buffer != NULL);
+
+  worker = orb_data->worker;
+  handle = worker->handle;
+
+  node = orb_data->node;
+  orb = &orb_data->orb;
+
+  chunk_size = orb_data->chunk_size;
+
+  buffer = orb_data->buffer;
+  page_pointer = orb_data->page_pointers;
+
+  switch (buffer->type)
+    {
+    case RBC_BUFFER_PAGE_TABLE:
+      page = buffer->data;
+
+      for (i = buffer->length; i > 0; i--, page++, page_pointer++)
+	{
+	  sbp2_write_data (handle, chunk_size, node, page_pointer,
+			   page->data, page->length);
+
+	  page->data = NULL;
+	}
+
+      break;
+
+    case RBC_BUFFER_SIMPLE:
+      sbp2_write_data (handle, chunk_size, node, &orb->data_desc,
+		       buffer->data, buffer->length);
+
+      buffer->data = NULL;
+      break;
+
+    case RBC_BUFFER_EMPTY:
+      /* do nothing */
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static void
+free_orb_data (gpointer data)
+{
+  ORBData *orb_data = data;
+
+  g_return_if_fail (orb_data != NULL);
+
+  if (orb_data->status)
+    g_free (orb_data->status);
+
+  if (orb_data->buffer)
+    rbc_buffer_destroy (orb_data->buffer);
+
+  if (orb_data->page_pointers)
+    g_free (orb_data->page_pointers);
+
+  g_free (orb_data);
+}
+
+static gint
+bus_reset_handler (raw1394handle_t handle,
+                   guint           generation)
+{
+  SBP2Worker *worker;
+
+  g_return_val_if_fail (handle != NULL, -1);
+
+  raw1394_update_generation (handle, generation);
+
+  worker = raw1394_get_userdata (handle);
+
+  g_return_val_if_fail (worker != NULL, -1);
+
+  worker->state = STATE_RESET;
+
+  worker->unattached = TRUE;
+
+  return 0;
+}

Added: trunk/libsbp2/sbp2worker.h
==============================================================================
--- trunk/libsbp2/sbp2worker.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/libsbp2/sbp2worker.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,57 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __SBP2_WORKER_H__
+#define __SBP2_WORKER_H__
+
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <librbc/rbcdisk.h>
+
+#include <libsbp2/sbp2pointer.h>
+#include <libsbp2/sbp2login.h>
+
+
+typedef struct _SBP2Worker SBP2Worker;
+
+
+SBP2Worker *sbp2_worker_new       (RBCDisk      *disk,
+                                   GMainContext *context,
+				   gint          port,
+				   guint         login_ID);
+
+void        sbp2_worker_destroy   (SBP2Worker   *worker);
+
+gboolean    sbp2_worker_login     (SBP2Worker   *worker,
+				   nodeid_t      node,
+				   SBP2Pointer  *pointer,
+				   SBP2LoginORB *orb);
+
+gboolean    sbp2_worker_reconnect (SBP2Worker   *worker,
+                                   nodeid_t      node);
+
+
+#endif /* __SBP2_WORKER_H__ */

Added: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/Makefile.am	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,35 @@
+bin_PROGRAMS = endpoint
+
+AM_CPPFLAGS = \
+	-DSYSCONFDIR=\""$(sysconfdir)"\"
+
+INCLUDES = \
+	-I$(top_srcdir)		\
+	$(GLIB_CFLAGS)		\
+	$(LIBRAW1394_CPPFLAGS)	\
+	-I$(includedir)
+
+endpoint_SOURCES = \
+	app.c		\
+	app.h		\
+	cleanup.c	\
+	cleanup.h	\
+	main.c		\
+	manager.c	\
+	manager.h	\
+	process.c	\
+	process.h	\
+	util.c		\
+	util.h		\
+	wire.c		\
+	wire.h		\
+	worker.c	\
+	worker.h
+
+endpoint_LDADD = \
+	$(top_builddir)/libsbp2/libsbp2.a	\
+	$(top_builddir)/librbc/librbc.a		\
+	$(top_builddir)/libjconfig/libjconfig.a	\
+	$(GLIB_LIBS)				\
+        $(LIBRAW1394_LIBS)
+

Added: trunk/src/app.c
==============================================================================
--- trunk/src/app.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/app.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,354 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <libjconfig/jiterator.h>
+#include <libjconfig/jconfig.h>
+
+#include <librbc/rbcdisk.h>
+
+#include "app.h"
+
+
+#define MAX_LOGINS_MIN              1
+#define MAX_LOGINS_MAX              64
+#define MAX_LOGINS_DEFAULT          12
+
+#define MAX_LOGINS_PER_PORT_MIN     1
+#define MAX_LOGINS_PER_PORT_MAX     64
+#define MAX_LOGINS_PER_PORT_DEFAULT 4
+
+#define DISK_NUMBER_MIN             0
+#define DISK_NUMBER_MAX             15
+
+#define ORDER_MIN                   0
+#define ORDER_MAX                   1048576
+
+
+static gboolean load_global_options   (EndpointApp   *app,
+				       JConfig       *cf);
+static gboolean load_disks            (EndpointApp   *app,
+				       JConfig       *cf);
+static gboolean load_partitions       (EndpointApp   *app,
+				       JConfig       *cf);
+
+static gboolean get_int_attribute     (JConfigStanza *cfs,
+				       const gchar   *name,
+				       gint          *value,
+				       gint           min,
+				       gint           max);
+static gboolean get_boolean_attribute (JConfigStanza *cfs,
+                                       const gchar   *name,
+				       gboolean      *value);
+
+
+EndpointApp *
+endpoint_app_new (const gchar *configfile)
+{
+  EndpointApp *app;
+  JConfig     *cf;
+
+  g_return_val_if_fail (configfile != NULL, NULL);
+
+  cf = j_config_parse_file (configfile);
+
+  if (!cf)
+    return NULL;
+ 
+  app = g_new0 (EndpointApp, 1);
+ 
+  app->max_logins = MAX_LOGINS_DEFAULT;
+  app->max_logins_per_port = MAX_LOGINS_PER_PORT_DEFAULT;
+  app->honor_exclusive = FALSE;
+
+  app->disks = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+				      NULL, (GDestroyNotify) rbc_disk_destroy);
+
+  if (!load_global_options (app, cf) || !load_disks (app, cf))
+    {
+      endpoint_app_destroy (app);
+      app = NULL;
+    }
+
+  j_config_free (cf);
+
+  /* TODO: multiple LUN */
+  {
+    RBCDisk *disk;
+
+    disk = g_hash_table_lookup (app->disks, GINT_TO_POINTER (0));
+
+    if (disk == NULL || rbc_disk_get_type (disk) == RBC_DISK_VIRTUAL)
+      {
+	endpoint_app_destroy (app);
+	app = NULL;
+      }
+  }
+
+  return app;
+}
+
+void
+endpoint_app_destroy (EndpointApp *app)
+{
+  g_return_if_fail (app != NULL);
+
+  g_hash_table_destroy (app->disks);
+  g_free (app);
+
+  return;
+}
+
+#define CHECK(cond) G_STMT_START {	\
+  if (!(cond))				\
+    {					\
+      ret = FALSE;			\
+      break;				\
+    }					\
+} G_STMT_END
+
+static gboolean
+load_global_options (EndpointApp *app,
+		     JConfig     *cf)
+{
+  JIterator     *iter;
+  JConfigStanza *cfs;
+  gboolean       ret = TRUE;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (cf != NULL, FALSE);
+
+  iter = j_config_get_stanzas (cf, "global", NULL, 0);
+
+  /* FIXME: hmm, allow only one global stanza? */
+  while (j_iterator_has_more (iter))
+    {
+      cfs = j_iterator_get_next (iter);
+
+      CHECK (get_int_attribute (cfs, "max_logins",
+				&app->max_logins,
+	                        MAX_LOGINS_MIN,
+				MAX_LOGINS_MAX));
+      CHECK (get_int_attribute (cfs, "max_logins_per_port",
+				&app->max_logins_per_port,
+	                        MAX_LOGINS_PER_PORT_MIN,
+				MAX_LOGINS_PER_PORT_MAX));
+
+      CHECK (get_boolean_attribute (cfs, "honor_exclusive_login",
+	                            &app->honor_exclusive));
+    }
+
+  j_iterator_free (iter);
+
+  if (ret && app->max_logins < app->max_logins_per_port)
+    {
+      /* ERROR MESSAGE */
+      return FALSE;
+    }
+
+  return ret;
+}
+
+static gboolean
+load_disks (EndpointApp *app,
+            JConfig     *cf)
+{
+  JIterator     *iter;
+  JConfigStanza *cfs;
+  gchar         *type_string;
+  RBCDiskType    type;
+  gint           number;
+  gchar         *filename;
+  RBCDisk       *disk = NULL;
+  gboolean       ret = TRUE;
+
+  iter = j_config_get_stanzas (cf, "disk", NULL, 0);
+
+  while (j_iterator_has_more (iter))
+    {
+      cfs = j_iterator_get_next (iter);
+
+      disk = NULL;
+
+      CHECK (type_string = j_config_get_attribute (cfs, "type"));
+
+      if (g_ascii_strcasecmp (type_string, "physical") == 0)
+	{
+	  type = RBC_DISK_PHYSICAL;
+	}
+      else if (g_ascii_strcasecmp (type_string, "virtual") == 0)
+	{
+	  type = RBC_DISK_VIRTUAL;
+	}
+      else
+	{
+	  ret = FALSE;
+	  break;
+	}
+
+      CHECK (get_int_attribute (cfs, "number",
+	                        &number,
+				DISK_NUMBER_MIN,
+				DISK_NUMBER_MAX));
+
+      disk = rbc_disk_new (type);
+
+      if (type == RBC_DISK_PHYSICAL)
+	{
+	  CHECK (filename = j_config_get_attribute (cfs, "filename"));
+	  CHECK (rbc_disk_set_filename (disk, filename));
+	}
+
+      /* FIXME: hmm, check for dupes? */
+      g_hash_table_insert (app->disks, GINT_TO_POINTER (number), disk);
+    }
+
+  if (!ret)
+    {
+      if (disk)
+	rbc_disk_destroy (disk);
+
+      return FALSE;
+    }
+  else
+    return load_partitions (app, cf);
+}
+
+static gboolean
+load_partitions (EndpointApp *app,
+                 JConfig     *cf)
+{
+  JIterator     *iter;
+  JConfigStanza *cfs;
+  gint           number;
+  gchar         *filename;
+  gint           order;
+  RBCDisk       *disk;
+  gboolean       ret = TRUE;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (cf != NULL, FALSE);
+
+  iter = j_config_get_stanzas (cf, "partition", NULL, 0);
+
+  while (j_iterator_has_more (iter))
+    {
+      cfs = j_iterator_get_next (iter);
+
+      CHECK (get_int_attribute (cfs, "disk",
+				&number,
+	                        DISK_NUMBER_MIN,
+				DISK_NUMBER_MAX));
+
+      CHECK (filename = j_config_get_attribute (cfs, "filename"));
+
+      CHECK (order = get_int_attribute (cfs, "order",
+	                                &order,
+					ORDER_MIN,
+					ORDER_MAX));
+
+      CHECK (disk = g_hash_table_lookup (app->disks, GINT_TO_POINTER (number)));
+
+      CHECK (rbc_disk_get_type (disk) == RBC_DISK_VIRTUAL);
+
+      rbc_disk_add_partition (disk, filename, order);
+    }
+
+  j_iterator_free (iter);
+
+  return ret;
+}
+
+static gboolean
+get_int_attribute (JConfigStanza *cfs,
+		   const gchar   *name,
+		   gint          *value,
+		   gint           min,
+		   gint           max)
+{
+  gchar    *buf, *end;
+  glong     number;
+  gboolean  ret = TRUE;
+
+  g_return_val_if_fail (cfs != NULL, FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  buf = j_config_get_attribute (cfs, name);
+
+  if (buf)
+    {
+      number = strtol (buf, &end, 0);
+
+      if (*buf && !*end && number >= min && number <= max)
+	*value = number;
+      else
+	ret = FALSE;
+    }
+
+  g_free (buf);
+
+  return ret;
+}
+
+static gboolean
+get_boolean_attribute (JConfigStanza *cfs,
+		       const gchar   *name,
+		       gboolean      *value)
+{
+  gchar    *buf;
+  gboolean  ret = TRUE;
+
+  g_return_val_if_fail (cfs != NULL, FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  buf = j_config_get_attribute (cfs, name);
+
+  if (buf)
+    {
+      if ((g_ascii_strcasecmp (buf, "on") == 0) ||
+	  (g_ascii_strcasecmp (buf, "true") == 0) ||
+	  (g_ascii_strcasecmp (buf, "yes") == 0) ||
+	  (g_ascii_strcasecmp (buf, "1") == 0))
+	{
+	  *value = TRUE;
+	}
+      else if ((g_ascii_strcasecmp (buf, "off") == 0) ||
+	       (g_ascii_strcasecmp (buf, "false") == 0) ||
+	       (g_ascii_strcasecmp (buf, "no") == 0) ||
+	       (g_ascii_strcasecmp (buf, "0") == 0))
+	{
+	  *value = FALSE;
+	}
+      else
+	ret = FALSE;
+    }
+
+  g_free (buf);
+
+  return ret;
+}

Added: trunk/src/app.h
==============================================================================
--- trunk/src/app.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/app.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,59 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __APP_H__
+#define __APP_H__
+
+
+#include <glib.h>
+
+
+typedef struct _EndpointApp EndpointApp;
+
+struct _EndpointApp
+{
+  GMainContext *context;
+  GMainLoop    *loop;
+
+  gchar        *ps_name;
+  gint          ps_len;
+
+  gint          max_logins;
+  gint          max_logins_per_port;
+  gboolean      honor_exclusive;
+
+  GHashTable   *disks;
+
+  gpointer      cleanup;
+  gpointer      wire_funcs;
+
+  gpointer      user_data;
+};
+
+
+EndpointApp  *endpoint_app_new     (const gchar *configfile);
+
+void          endpoint_app_destroy (EndpointApp *app);
+
+
+#endif /* __APP_H__ */

Added: trunk/src/cleanup.c
==============================================================================
--- trunk/src/cleanup.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/cleanup.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,131 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "app.h"
+#include "cleanup.h"
+#include "util.h"
+
+
+static gboolean do_cleanup      (GIOChannel   *channel,
+                                 GIOCondition  condition,
+				 EndpointApp  *app);
+
+static void     trigger_cleanup (gint          signum);
+
+
+static gint cleanup_signum = SIGTERM;
+static gint cleanup_fd = -1;
+
+
+gboolean
+cleanup_register (EndpointApp *app,
+		  CleanupFunc  cleanup)
+{
+  gint        cpipe[2];
+  GIOChannel *channel;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (cleanup != NULL, FALSE);
+
+  cleanup_unregister (app);
+
+  if (pipe (cpipe) < 0)
+    return FALSE;
+
+  app->cleanup = cleanup;
+
+  cleanup_fd = cpipe[1];
+
+  channel = util_io_channel_new (cpipe[0]);
+
+  util_io_add_watch (app->context, channel,
+                     G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+		     (GIOFunc) do_cleanup, app);
+
+  signal (SIGINT,  trigger_cleanup);
+  signal (SIGQUIT, trigger_cleanup);
+  signal (SIGABRT, trigger_cleanup);
+  signal (SIGTERM, trigger_cleanup);
+ 
+  return TRUE; 
+}
+
+void
+cleanup_unregister (EndpointApp *app)
+{
+  g_return_if_fail (app != NULL);
+
+  if (app->cleanup)
+    {
+      signal (SIGINT,  SIG_DFL);
+      signal (SIGQUIT, SIG_DFL);
+      signal (SIGABRT, SIG_DFL);
+      signal (SIGTERM, SIG_DFL);
+
+      app->cleanup = NULL;
+
+      g_return_if_fail (cleanup_fd > 0);
+
+      close (cleanup_fd);
+      cleanup_fd = -1;
+    }
+}
+
+static gboolean
+do_cleanup (GIOChannel   *channel,
+	    GIOCondition  condition,
+            EndpointApp  *app)
+{
+  CleanupFunc cleanup;
+  gint        signum;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->cleanup != NULL, FALSE);
+
+  signum = cleanup_signum;
+
+  signal (signum, SIG_IGN);
+
+  cleanup = app->cleanup;
+  cleanup (app, signum);
+
+  app->context = NULL;
+  g_main_loop_quit (app->loop);
+
+  signal (signum, SIG_DFL);
+
+  return FALSE;
+}
+
+static void
+trigger_cleanup (gint signum)
+{
+  cleanup_signum = signum;
+  write (cleanup_fd, "C", 1);
+}

Added: trunk/src/cleanup.h
==============================================================================
--- trunk/src/cleanup.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/cleanup.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,42 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __CLEANUP_H__
+#define __CLEANUP_H__
+
+
+#include <glib.h>
+
+#include "app.h"
+
+
+typedef void (*CleanupFunc) (EndpointApp *app,
+			     gint         signum);
+
+
+gboolean cleanup_register   (EndpointApp *app,
+			     CleanupFunc  cleanup);
+void     cleanup_unregister (EndpointApp *app);
+
+
+#endif /* __CLEANUP_H__ */

Added: trunk/src/main.c
==============================================================================
--- trunk/src/main.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/main.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,441 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "app.h"
+#include "cleanup.h"
+#include "manager.h"
+#include "process.h"
+#include "util.h"
+#include "wire.h"
+#include "worker.h"
+
+
+#define CONFIG_FILE SYSCONFDIR "/endpoint.conf"
+
+#define MASTER_NAME  "endpoint"
+#define MANAGER_NAME "endptmgr"
+#define WORKER_NAME  "endptio"
+
+
+/* #define DEBUG_MANAGER */
+/* #define DEBUG_WORKER */
+
+
+typedef struct _ProcessInfo ProcessInfo;
+
+struct _ProcessInfo
+{
+  Process    *manager;
+  GHashTable *workers;
+};
+
+
+static gboolean start_manager      (EndpointApp      *app);
+
+static gboolean login_worker       (EndpointApp      *app,
+				    WireLogin        *message);
+static gboolean reconnect_worker   (EndpointApp      *app,
+				    WireReconnect    *message);
+static gboolean logout_worker      (EndpointApp      *app,
+				    WireLogout       *message);
+
+static gboolean worker_active      (EndpointApp      *app,
+				    WireWorkerActive *message);
+
+static void     manager_reap       (Process          *process,
+				    gint              status);
+static void     worker_reap        (Process          *process,
+				    gint              status);
+
+static void     kill_all_processes (EndpointApp      *app,
+				    gint              signum);
+
+static void     set_ps_name        (EndpointApp      *app,
+				    const gchar      *name);
+
+
+static WireMsgFuncs wire_funcs = {
+  .login         = login_worker,
+  .reconnect     = reconnect_worker,
+  .logout        = logout_worker,
+  .worker_active = worker_active
+};
+
+
+#ifdef DEBUG_MANAGER
+#define debug_manager() debug_process ()
+#else
+#define debug_manager() G_STMT_START{ (void)0; }G_STMT_END
+#endif
+
+#ifdef DEBUG_WORKER
+#define debug_worker() debug_process ()
+#else
+#define debug_worker() G_STMT_START{ (void)0; }G_STMT_END
+#endif
+
+
+static inline void
+debug_process (void)
+{
+  GLogLevelFlags fatal_mask;
+
+  fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+  fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+  g_log_set_always_fatal (fatal_mask);
+
+  raise (SIGSTOP);
+}
+
+
+int
+main (int   argc,
+      char *argv[])
+{
+  EndpointApp *app;
+  gchar       *configfile = CONFIG_FILE;
+  ProcessInfo *pinfo;
+
+  if (argc > 1)
+    configfile = argv[1];
+
+  app = endpoint_app_new (configfile);
+
+  if (!app)
+    {
+      /* ERROR MESSAGE */
+      exit (1);
+    }
+
+  app->ps_name = argv[0];
+  app->ps_len = strlen(argv[0]);
+
+  if (app->ps_len < strlen (MASTER_NAME))
+    {
+      /* ERROR MESSAGE */
+      exit (1);
+    }
+
+  set_ps_name (app, MASTER_NAME);
+
+  app->wire_funcs = &wire_funcs;
+
+  if (!wire_init ())
+    {
+      /* ERROR MESSAGE */
+      exit (1);
+    }
+
+  pinfo = g_new (ProcessInfo, 1);
+
+  pinfo->manager = NULL;
+  pinfo->workers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                          NULL, g_free);
+
+  app->user_data = pinfo;
+
+  app->context = g_main_context_new ();
+
+  setpgid (0, 0);
+
+#if 0
+  if (!cleanup_register (app, kill_all_processes))
+    {
+      /* ERROR MESSAGE */
+      exit (1);
+    }
+#endif
+
+  signal (SIGPIPE, SIG_IGN);
+
+  util_run_first_add (app->context, (GSourceFunc) start_manager, app);
+
+  while (app->context)
+    {
+      app->loop = g_main_loop_new (app->context, TRUE);
+      g_main_context_unref (app->context);
+
+      g_main_loop_run (app->loop);
+      g_main_loop_unref (app->loop);
+
+      if (pinfo)
+	{
+	  g_hash_table_destroy (pinfo->workers);
+	  g_free (pinfo->manager);
+
+	  g_free (pinfo);
+	  pinfo = NULL;
+	}
+    }
+
+  while (wait (NULL) > 0);
+
+  return 0;
+}
+
+static gboolean
+start_manager (EndpointApp *app)
+{
+  Process *process;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+
+  process = process_start (app, manager_reap);
+
+  if (!process)
+    {
+      /* ERROR MESSAGE */
+      exit (1);
+    }
+
+  if (process->pid == 0)
+    {
+      set_ps_name (app, MANAGER_NAME);
+
+      debug_manager ();
+
+      manager_process (app, process);
+      g_free (process);
+    }
+  else
+    {
+      ProcessInfo *pinfo;
+
+      pinfo = app->user_data;
+      pinfo->manager = process;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+login_worker (EndpointApp *app,
+              WireLogin   *message)
+{
+  Process     *process;
+  ProcessInfo *pinfo;
+  gpointer     ret_message;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+
+  pinfo = app->user_data;
+
+  process = process_start (app, worker_reap);
+
+  if (!process)
+    {
+      ret_message = wire_message_worker_died (message->login_ID);
+      wire_message_send (pinfo->manager->writer, ret_message, app);
+
+      return TRUE;
+    }
+
+  if (process->pid == 0)
+    {
+      set_ps_name (app, WORKER_NAME);
+
+      debug_worker ();
+
+      worker_process (app, process, message);
+      g_free (process);
+    }
+  else
+    {
+      process->login_ID = message->login_ID;
+
+      g_hash_table_insert (pinfo->workers,
+	                   GUINT_TO_POINTER (message->login_ID), process);
+    }
+ 
+  return TRUE;
+}
+
+static gboolean
+reconnect_worker (EndpointApp   *app,
+		  WireReconnect *message)
+{
+  Process     *process;
+  ProcessInfo *pinfo;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+
+  pinfo = app->user_data;
+
+  process = g_hash_table_lookup (pinfo->workers,
+                                 GUINT_TO_POINTER (message->login_ID));
+
+  if (process)
+    {
+      WireReconnect *copy;
+
+      copy = wire_message_copy (message);
+      wire_message_send (process->writer, copy, app);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+logout_worker (EndpointApp *app,
+	       WireLogout  *message)
+{
+  Process     *process;
+  ProcessInfo *pinfo;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+  
+  pinfo = app->user_data;
+
+  process = g_hash_table_lookup (pinfo->workers,
+                                 GUINT_TO_POINTER (message->login_ID));
+
+  if (process)
+    kill (process->pid, SIGTERM);
+
+  return TRUE;
+}
+
+static gboolean
+worker_active (EndpointApp      *app,
+	       WireWorkerActive *message)
+{
+  ProcessInfo      *pinfo;
+  WireWorkerActive *copy;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+
+  pinfo = app->user_data;
+
+  copy = wire_message_copy (message);
+  wire_message_send (pinfo->manager->writer, copy, app);
+
+  return TRUE;
+}
+
+static void
+manager_reap (Process *process,
+	      gint     status)
+{             
+  ProcessInfo *pinfo;
+
+  g_return_if_fail (process != NULL);
+  g_return_if_fail (process->app != NULL);
+  g_return_if_fail (process->app->user_data != NULL);
+
+  pinfo = process->app->user_data;
+
+  if (WIFEXITED (status))
+    {
+      gint exit_status = WEXITSTATUS (status);
+
+      if (exit_status != 0)
+	{
+	  /* ERROR MESSAGE */
+	}
+
+      exit (exit_status);
+    }
+  else if (WIFSIGNALED (status))
+    {
+      gint signum = WTERMSIG (status);
+
+      g_printerr ("Manager exited (%s)\n", g_strsignal (signum));
+      exit (5);
+    }
+  else
+    exit (6);
+}
+
+static void
+worker_reap (Process *process,
+	     gint     status)
+{
+  ProcessInfo *pinfo;
+  gpointer     message;
+
+  g_return_if_fail (process != NULL);
+  g_return_if_fail (process->app != NULL);
+  g_return_if_fail (process->app->user_data != NULL);
+
+  pinfo = process->app->user_data;
+
+  message = wire_message_worker_died (process->login_ID);
+  wire_message_send (pinfo->manager->writer, message, process->app);
+
+  g_hash_table_steal (pinfo->workers, GUINT_TO_POINTER (process->login_ID));
+
+  if (WIFEXITED (status))
+    {
+      g_printerr ("Worker exited: %d\n", WEXITSTATUS (status));
+    }
+  else if (WIFSIGNALED (status))
+    {
+      gint signum = WTERMSIG (status);
+
+      g_printerr ("Worker exited (%s)\n", g_strsignal (signum));
+    }
+}
+
+static void
+kill_all_processes (EndpointApp *app,
+		    gint         signum)
+{
+  kill (0, signum);
+
+  app->context = NULL;
+  g_main_loop_quit (app->loop);
+}
+
+static void
+set_ps_name (EndpointApp *app,
+	     const gchar *name)
+{
+  gint len;
+
+  g_return_if_fail (app != NULL);
+  g_return_if_fail (name != NULL);
+
+  len = strlen (name);
+
+  strcpy (app->ps_name, name);
+  memset (app->ps_name + len, 0, app->ps_len - len);
+}

Added: trunk/src/manager.c
==============================================================================
--- trunk/src/manager.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/manager.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,538 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <libsbp2/sbp2byteswap.h>
+#include <libsbp2/sbp2constants.h>
+#include <libsbp2/sbp2main.h>
+#include <libsbp2/sbp2manager.h>
+#include <libsbp2/sbp2raw1394.h>
+
+#include "app.h"
+#include "cleanup.h"
+#include "manager.h"
+#include "util.h"
+#include "wire.h"
+
+
+typedef enum
+{
+  LOGIN_EMPTY,
+  LOGIN_PENDING,
+  LOGIN_ACTIVE,
+  LOGIN_RECONNECT,
+  LOGIN_DEAD
+} LoginStatus;
+
+
+typedef struct _ManagerState ManagerState;
+typedef struct _Port Port;
+typedef struct _Login Login;
+
+struct _ManagerState
+{
+  EndpointApp *app;
+
+  gint         num_ports;
+  Port        *ports;
+
+  gint         num_logins;
+  Login       *logins;
+
+  gboolean     exclusive;
+
+  GIOChannel  *reader;
+  GIOChannel  *writer;
+};
+
+struct _Port
+{
+  ManagerState *state;
+
+  gint          number;
+
+  SBP2Manager  *manager;
+
+  gint          num_logins;
+};
+
+struct _Login
+{
+  LoginStatus  status;
+
+  guint        login_ID;
+  Port        *port;
+
+  guint        timeout;
+
+  guint64      EUI_64;
+
+  nodeid_t     node;
+  SBP2Pointer  status_FIFO;
+
+  guint8       sbp2_status[8];
+};
+
+
+static gboolean query_logins       (SBP2ManagerAction       *action,
+				    SBP2QueryLoginsORB      *orb,
+				    SBP2QueryLoginsResponse *resp);
+static gboolean login              (SBP2ManagerAction       *action,
+				    SBP2LoginORB            *orb,
+				    SBP2LoginResponse       *resp);
+static gboolean reconnect          (SBP2ManagerAction       *action,
+				    SBP2ReconnectORB        *orb);
+static gboolean logout             (SBP2ManagerAction       *action,
+				    SBP2LogoutORB           *orb);
+
+static gboolean worker_active      (EndpointApp             *app,
+				    WireWorkerActive        *message);
+static gboolean worker_died        (EndpointApp             *app,
+                                    WireWorkerDied          *message);
+
+static gboolean find_free_login_ID (ManagerState            *state,
+                                    guint                   *login_ID);
+static gboolean expire_login       (Login                   *login);
+
+static void     bus_reset          (SBP2Manager             *manager,
+				    gpointer                 user_data);
+
+
+static SBP2ManagerFuncs manager_funcs = {
+  .bus_reset    = bus_reset,
+
+  .login        = login,
+  .query_logins = query_logins,
+  .reconnect    = reconnect,
+  .logout       = logout
+};
+
+static WireMsgFuncs wire_funcs = {
+  .worker_active = worker_active,
+  .worker_died   = worker_died
+};
+
+
+void
+manager_process (EndpointApp *app,
+		 Process     *process)
+{
+  ManagerState    *state;
+  gint             num_ports, i;
+  Port            *port;
+
+  g_return_if_fail (app != NULL);
+  g_return_if_fail (process != NULL);
+
+  num_ports = sbp2_raw1394_get_num_ports ();
+
+  if (num_ports < 1)
+    exit (1);
+
+  app->wire_funcs = &wire_funcs;
+
+  state = g_new (ManagerState, 1);
+
+  state->app = app;
+
+  state->num_ports = num_ports;
+  state->ports = g_new0 (Port, num_ports);
+
+  state->num_logins = 0;
+  state->logins = g_new0 (Login, app->max_logins);
+
+  state->reader = process->reader;
+  state->writer = process->writer;
+
+  app->user_data = state;
+
+  for (i = 0; i < num_ports; i++)
+    {
+       port = &state->ports[i];
+
+       port->state = state;
+
+       port->number = i;
+
+       port->manager = sbp2_manager_new (&manager_funcs, app->context, i, port);
+       
+       if (!port->manager)
+         exit (2);
+
+       port->num_logins = 0;
+    }
+}
+
+static gboolean
+query_logins (SBP2ManagerAction       *action,
+              SBP2QueryLoginsORB      *orb,
+	      SBP2QueryLoginsResponse *resp)
+{
+  Port *port;
+
+  g_return_val_if_fail (action != NULL, FALSE);
+  g_return_val_if_fail (action->user_data != NULL, FALSE);
+  g_return_val_if_fail (orb != NULL, FALSE);
+  g_return_val_if_fail (resp != NULL, FALSE);
+
+  port = action->user_data;
+
+  resp->length = 4 + 12 * port->num_logins;
+  resp->max_logins = port->state->app->max_logins_per_port;
+
+  return TRUE;
+}
+
+static gboolean
+login (SBP2ManagerAction *action,
+       SBP2LoginORB      *orb,
+       SBP2LoginResponse *resp)
+{
+  Port         *port;
+  ManagerState *state;
+  EndpointApp  *app;
+  guint         login_ID;
+  Login        *login;
+  gpointer      message;
+
+  g_return_val_if_fail (action != NULL, FALSE);
+  g_return_val_if_fail (action->user_data != NULL, FALSE);
+  g_return_val_if_fail (orb != NULL, FALSE);
+  g_return_val_if_fail (resp != NULL, FALSE);
+
+  port = action->user_data;
+
+  state = port->state;
+  app = state->app;
+
+  if (app->honor_exclusive && state->num_logins > 0 &&
+      (orb->exclusive || state->exclusive))
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_ACCESS_DENIED;
+      return TRUE;
+    }
+
+  if (port->num_logins == app->max_logins_per_port)
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_ACCESS_DENIED;
+      return TRUE;
+    }
+
+  if (!find_free_login_ID (state, &login_ID))
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_RESOURCES_UNAVAILABLE;
+      return TRUE;
+    }
+
+  orb->lun = 0; /* TODO: multiple LUN */
+
+  if (!g_hash_table_lookup (app->disks, GINT_TO_POINTER (orb->lun)))
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_LU_NOT_SUPPORTED;
+      return TRUE;
+    }
+
+  if (app->honor_exclusive)
+    state->exclusive = orb->exclusive;
+
+  login = &state->logins[login_ID];
+
+  login->status = LOGIN_PENDING;
+
+  login->login_ID = login_ID;
+  login->port = port;
+
+  login->node = action->node;
+  memcpy (&login->status_FIFO, &orb->status_FIFO, sizeof (login->status_FIFO));
+
+  memcpy (&login->sbp2_status, action->status, sizeof (login->sbp2_status));
+
+  port->num_logins++;
+  state->num_logins++;
+
+  message = wire_message_login (login_ID, port->number,
+                                action->node, action->pointer, orb);
+  wire_message_send (state->writer, message, app);
+
+  return FALSE;
+}
+
+static gboolean
+reconnect (SBP2ManagerAction *action,
+	   SBP2ReconnectORB  *orb)
+{
+  Port         *port;
+  ManagerState *state;
+  EndpointApp  *app;
+  guint         login_ID;
+  Login        *login;
+
+  g_return_val_if_fail (action != NULL, FALSE);
+  g_return_val_if_fail (action->user_data != NULL, FALSE);
+  g_return_val_if_fail (orb != NULL, FALSE);
+
+  port = action->user_data;
+
+  state = port->state;
+  app = state->app;
+
+  login_ID = orb->login_ID;
+
+  if (login_ID >= app->max_logins)
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_LOGIN_ID_NOT_RECOGNIZED;
+      return TRUE;
+    }
+
+  login = &state->logins[login_ID];
+
+  if (login->status != LOGIN_RECONNECT)
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_LOGIN_ID_NOT_RECOGNIZED;
+      return TRUE;
+    }
+
+  if (login->timeout != 0)
+    {
+      util_source_remove (app->context, login->timeout);
+      login->timeout = 0;
+    }
+
+  login->status = LOGIN_ACTIVE;
+
+  return TRUE;
+}
+
+static gboolean
+logout (SBP2ManagerAction *action,
+	SBP2LogoutORB     *orb)
+{
+  Port         *port;
+  ManagerState *state;
+  EndpointApp  *app;
+  guint         login_ID;
+  Login        *login;
+
+  g_return_val_if_fail (action != NULL, FALSE);
+  g_return_val_if_fail (action->user_data != NULL, FALSE);
+  g_return_val_if_fail (orb != NULL, FALSE);
+
+  port = action->user_data;
+
+  state = port->state;
+  app = state->app;
+  
+  login_ID = orb->login_ID;
+
+  if (login_ID >= app->max_logins)
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_LOGIN_ID_NOT_RECOGNIZED;
+      return TRUE;
+    }
+
+  login = &state->logins[login_ID];
+
+  if (login->status == LOGIN_EMPTY)
+    {
+      action->status->sbp_status = SBP2_STATUS_REQUEST_LOGIN_ID_NOT_RECOGNIZED;
+      return TRUE;
+    }
+
+  if (login->timeout != 0)
+    {
+      util_source_remove (app->context, login->timeout);
+      login->timeout = 0;
+    }
+
+  expire_login (login);
+
+  return TRUE;
+}
+
+static gboolean
+worker_active (EndpointApp      *app,
+	       WireWorkerActive *message)
+{
+  ManagerState *state;
+  Login        *login;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+  g_return_val_if_fail (message->login_ID < app->max_logins, FALSE);
+
+  state = app->user_data;
+
+  login = &state->logins[message->login_ID];
+
+  login->status = LOGIN_ACTIVE;
+
+  return TRUE;
+}
+
+static gboolean
+worker_died (EndpointApp    *app,
+             WireWorkerDied *message)
+{
+  ManagerState    *state;
+  Login           *login;
+  Port            *port;
+  SBP2Status      *status;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+  g_return_val_if_fail (message->login_ID < app->max_logins, FALSE);
+
+  state = app->user_data;
+
+  login = &state->logins[message->login_ID];
+ 
+  port = login->port;
+
+  switch (login->status)
+    {
+    case LOGIN_PENDING:
+      status = g_memdup (&login->sbp2_status, sizeof (login->sbp2_status));
+      status->sbp_status = SBP2_STATUS_REQUEST_RESOURCES_UNAVAILABLE;
+
+      sbp2_manager_write_status (port->manager,
+	                         login->node, &login->status_FIFO,
+				 status);
+      break;
+
+    case LOGIN_ACTIVE:
+      /* sbp2_manager_reset_bus (port->manager); */
+      break;
+
+    case LOGIN_RECONNECT:
+      /*
+      status = g_memdup (&login->sbp2_status, sizeof (login->sbp2_status));
+      status->sbp_status = SBP2_STATUS_REQUEST_RESOURCES_UNAVAILABLE;
+
+      sbp2_manager_write_status (port->manager,
+	                         login->node, &login->status_FIFO,
+				 status);
+      */
+      break;
+
+    case LOGIN_DEAD:
+      /* do nothing */
+      break;
+
+    case LOGIN_EMPTY:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  login->status = LOGIN_EMPTY;
+
+  port->num_logins--;
+  state->num_logins--;
+
+  login->port = NULL;
+
+  return TRUE;
+}
+
+static gboolean
+find_free_login_ID (ManagerState *state,
+                    guint        *login_ID)
+{
+  gint i;
+
+  g_return_val_if_fail (state != NULL, FALSE);
+  g_return_val_if_fail (login_ID != NULL, FALSE);
+
+  for (i = 0; i < state->app->max_logins; i++)
+    {
+      if (state->logins[i].status == LOGIN_EMPTY)
+	{
+	  *login_ID = i;
+	  return TRUE;
+	}
+    } 
+
+  return FALSE;
+}
+
+static gboolean
+expire_login (Login *login)
+{
+  ManagerState *state;
+  gpointer      message;
+
+  g_return_val_if_fail (login != NULL, FALSE);
+
+  state = login->port->state;
+
+  login->status = LOGIN_DEAD;
+
+  message = wire_message_logout (login->login_ID);
+  wire_message_send (state->writer, message, state->app);
+
+  login->timeout = 0;
+
+  return FALSE;
+}
+
+static void
+bus_reset (SBP2Manager *manager,
+           gpointer     user_data)
+{
+  Port         *port = user_data;
+  ManagerState *state;
+  EndpointApp  *app;
+  gint          i;
+  Login        *login;
+
+  g_return_if_fail (manager != NULL);
+  g_return_if_fail (port != NULL);
+
+  state = port->state;
+  app = state->app;
+
+  for (i = 0; i < app->max_logins; i++)
+    {
+      login = &state->logins[i];
+
+      if (login->port != port)
+	continue;
+
+      if (login->status == LOGIN_ACTIVE)
+	{
+	  login->status = LOGIN_RECONNECT;
+
+	  login->timeout = util_timeout_add (app->context,
+	                                     (SBP2_RECONNECT_HOLD + 1) * 1000,
+					     (GSourceFunc) expire_login, login);
+	}
+      else if (login->status == LOGIN_PENDING)
+	expire_login (login);
+    }
+}

Added: trunk/src/manager.h
==============================================================================
--- trunk/src/manager.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/manager.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,36 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __MANAGER_H__
+#define __MANAGER_H__
+
+
+#include "app.h"
+#include "process.h"
+
+
+void manager_process (EndpointApp *app,
+		      Process     *process);
+
+
+#endif /* __MANAGER_H__ */

Added: trunk/src/process.c
==============================================================================
--- trunk/src/process.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/process.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,206 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "app.h"
+#include "cleanup.h"
+#include "process.h"
+#include "util.h"
+#include "wire.h"
+
+
+#define REAP_DELAY       1000
+#define REAP_RETRY_COUNT 5
+
+
+static void     close_descriptors (gint          except_reader,
+				   gint          except_writer);
+
+static gboolean reap_process      (GIOChannel   *channel,
+                                   GIOCondition  condition,
+				   Process      *process);
+static gboolean waitpid_timeout   (Process      *process);
+
+
+static inline void
+close_fd (gint fd)
+{
+  if (fd != -1)
+    close (fd);
+}
+
+Process *
+process_start (EndpointApp       *app,
+               ProcessReapNotify  notify)
+{
+  gint        ipipe[2] = { -1, -1 };
+  gint        opipe[2] = { -1, -1 };
+  gint        my_reader, sub_reader;
+  gint        my_writer, sub_writer;
+  GIOChannel *reader;
+  GIOChannel *writer;
+  gint        pid;
+  Process    *process;
+
+  g_return_val_if_fail (app != NULL, NULL);
+
+  if (pipe (ipipe) < 0 || pipe (opipe) < 0)
+    goto fail;
+
+  my_reader  = ipipe[0];
+  my_writer  = opipe[1];
+  sub_reader = opipe[0];
+  sub_writer = ipipe[1];
+
+  pid = fork ();
+
+  if (pid < 0)
+    goto fail;
+
+  process = g_new (Process, 1);
+
+  if (pid == 0)
+    {
+      //close_descriptors (sub_reader, sub_writer);
+
+      cleanup_unregister (app);
+
+      reader = util_io_channel_new (sub_reader);
+      writer = util_io_channel_new (sub_writer);
+
+      g_main_loop_quit (app->loop);
+
+      app->context = g_main_context_new ();
+
+      app->wire_funcs = NULL;
+      app->user_data = NULL;
+    }
+  else
+    {
+      close (sub_reader);
+      close (sub_writer);
+
+      reader = util_io_channel_new (my_reader);
+      writer = util_io_channel_new (my_writer);
+
+      util_io_add_watch (app->context, writer,
+			 G_IO_ERR | G_IO_HUP,
+			 (GIOFunc) reap_process, process);
+    }
+  
+  util_io_add_watch (app->context, reader,
+                     G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+		     (GIOFunc) wire_message_process, app);
+
+  process->app = app;
+
+  process->pid = pid;
+
+  process->reader = reader;
+  process->writer = writer;
+
+  process->notify = notify;
+  process->count = REAP_RETRY_COUNT;
+
+  return process;
+
+fail:
+  close_fd (ipipe[0]);
+  close_fd (ipipe[1]);
+  close_fd (opipe[0]);
+  close_fd (opipe[1]);
+
+  return NULL;
+}
+
+static void
+close_descriptors (gint except_reader,
+                   gint except_writer)
+{
+  glong open_max;
+  gint  i;
+
+  g_return_if_fail (except_reader > 3);
+  g_return_if_fail (except_writer > 3);
+
+  open_max = sysconf(_SC_OPEN_MAX);
+
+  for (i = 3; i < open_max; i++)
+    if (i != except_reader && i != except_writer)
+      close (i);
+}
+
+static gboolean
+reap_process (GIOChannel   *channel,
+	      GIOCondition  condition,
+	      Process      *process)
+{
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (process != NULL, FALSE);
+  g_return_val_if_fail (process->app->context != NULL, FALSE);
+
+  util_timeout_add (process->app->context, REAP_DELAY,
+                    (GSourceFunc) waitpid_timeout, process);
+
+  return FALSE;
+}
+
+static gboolean
+waitpid_timeout (Process *process)
+{
+  gint pid;
+  gint status;
+
+  g_return_val_if_fail (process != NULL, TRUE);
+  g_return_val_if_fail (process->pid > 0, TRUE);
+
+  process->count--;
+
+  pid = waitpid (process->pid, &status, WNOHANG);
+
+  if (pid == -1)
+    {
+      /* ERROR MESSAGE */
+    }
+  else if (pid == 0)
+    {
+      if (process->count == 0)
+	kill (process->pid, SIGTERM);
+      else if (process->count < 0)
+	kill (process->pid, SIGKILL);
+
+      return TRUE;
+    }
+
+  process->notify (process, status);
+  g_free (process);
+
+  return FALSE;
+}

Added: trunk/src/process.h
==============================================================================
--- trunk/src/process.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/process.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,58 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __PROCESS_H__
+#define __PROCESS_H__
+
+
+#include <glib.h>
+
+#include "app.h"
+
+
+typedef struct _Process Process;
+
+typedef void (*ProcessReapNotify) (Process *process,
+                                   gint     status);
+
+struct _Process
+{
+  EndpointApp       *app;
+
+  gint               pid;
+  guint              login_ID;
+
+  GIOChannel        *reader;
+  GIOChannel        *writer;
+
+  /* private */
+  ProcessReapNotify  notify;
+  gint               count;
+};
+
+
+Process *process_start (EndpointApp       *app,
+                        ProcessReapNotify  notify);
+
+
+#endif /* __PROCESS_H__ */

Added: trunk/src/util.c
==============================================================================
--- trunk/src/util.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/util.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,130 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <glib.h>
+
+
+static guint attach_source (GSource      *source,
+			    GMainContext *context,
+			    GSourceFunc   function,
+			    gpointer      data);
+
+
+GIOChannel *
+util_io_channel_new (gint fd)
+{
+  GIOChannel *channel;
+
+  g_return_val_if_fail (fd >= 0, NULL);
+
+  channel = g_io_channel_unix_new (fd);
+
+  g_io_channel_set_encoding (channel, NULL, NULL);
+  g_io_channel_set_buffered (channel, FALSE);
+  g_io_channel_set_close_on_unref (channel, TRUE);
+
+  return channel;
+}
+
+guint
+util_run_first_add (GMainContext *context,
+		    GSourceFunc   function,
+		    gpointer      data)
+{
+  GSource *source;
+
+  g_return_val_if_fail (function != NULL, 0);
+
+  source = g_idle_source_new ();
+  g_source_set_priority (source, G_PRIORITY_HIGH);
+
+  return attach_source (source, context, function, data);
+}
+
+guint
+util_timeout_add (GMainContext *context,
+		  guint         interval,
+		  GSourceFunc   function,
+		  gpointer      data)
+{
+  GSource *source;
+
+  g_return_val_if_fail (function != NULL, 0);
+
+  source = g_timeout_source_new (interval);
+
+  return attach_source (source, context, function, data);
+}
+
+guint
+util_io_add_watch (GMainContext *context,
+		   GIOChannel   *channel,
+		   GIOCondition  condition,
+		   GIOFunc       function,
+		   gpointer      data)
+{
+  GSource *source;
+
+  g_return_val_if_fail (channel != NULL, 0);
+  g_return_val_if_fail (function != NULL, 0);
+
+  source = g_io_create_watch (channel, condition);
+  g_io_channel_unref (channel);
+
+  return attach_source (source, context, (GSourceFunc) function, data);
+}
+
+static guint
+attach_source (GSource      *source,
+               GMainContext *context,
+	       GSourceFunc   function,
+	       gpointer      data)
+{
+  guint id;
+
+  g_return_val_if_fail (source != NULL, 0);
+  g_return_val_if_fail (function != NULL, 0);
+
+  g_source_set_callback (source, function, data, NULL);
+
+  id = g_source_attach (source, context);
+  g_source_unref (source);
+
+  return id;
+}
+
+gboolean
+util_source_remove (GMainContext *context,
+                    guint         tag)
+{
+  GSource *source;
+
+  g_return_val_if_fail (context != NULL, FALSE);
+
+  source = g_main_context_find_source_by_id (context, tag);
+
+  if (source)
+    g_source_destroy (source);
+
+  return source != NULL;
+}

Added: trunk/src/util.h
==============================================================================
--- trunk/src/util.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/util.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,50 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __UTIL_H__
+#define __UTIL_H__
+
+
+#include <glib.h>
+
+
+GIOChannel *util_io_channel_new (gint          fd);
+
+guint       util_run_first_add  (GMainContext *context,
+				 GSourceFunc   function,
+				 gpointer      data);
+guint       util_timeout_add    (GMainContext *context,
+				 guint         interval,
+				 GSourceFunc   function,
+				 gpointer      data);
+guint       util_io_add_watch   (GMainContext *context,
+				 GIOChannel   *channel,
+				 GIOCondition  condition,
+				 GIOFunc       function,
+				 gpointer      data);
+
+gboolean    util_source_remove  (GMainContext *context,
+                                 guint         tag);
+
+
+#endif /* __UTIL_H__ */

Added: trunk/src/wire.c
==============================================================================
--- trunk/src/wire.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/wire.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,376 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <libsbp2/sbp2login.h>
+
+#include "app.h"
+#include "util.h"
+#include "wire.h"
+
+
+/* Keep this updated with the biggest wire message structure */
+#define MAX_MESSAGE_LENGTH (sizeof (WireLogin))
+
+
+typedef struct _WireLoginID WireLoginID;
+
+typedef struct _WireBuffer WireBuffer;
+
+struct _WireBuffer
+{
+  gpointer  message;
+
+  gchar    *buffer;
+  gsize     length;
+};
+
+
+static gboolean read_data        (GIOChannel   *channel,
+				  WireBuffer   *wire_buffer);
+static gboolean write_data       (GIOChannel   *channel,
+                                  GIOCondition  condition,
+				  WireBuffer   *wire_buffer);
+
+static void     dispatch_message (EndpointApp  *app,
+                                  gpointer      message);
+
+
+static inline WireMessageType
+get_message_type (gpointer message)
+{
+  return ((WireHeader *) message)->type;
+}
+
+static inline gpointer
+alloc_message (WireMessageType type)
+{
+  WireHeader *header;
+
+  header = g_malloc0 (MAX_MESSAGE_LENGTH); /* valgrind safe */
+  header->type = type;
+
+  return header;
+}
+
+static inline gpointer
+alloc_message_login_ID (WireMessageType type,
+                        guint           login_ID)
+{
+  WireLoginID *message;
+
+  message = alloc_message (type);
+  message->login_ID = login_ID;
+
+  return message;
+}
+
+
+gboolean
+wire_init (void)
+{
+  return PIPE_BUF >= MAX_MESSAGE_LENGTH;
+}
+
+gboolean
+wire_message_send (GIOChannel  *channel,
+                   gpointer     message,
+		   EndpointApp *app)
+{
+  WireBuffer *wire_buffer;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->context != NULL, FALSE);
+
+  wire_buffer = g_new0 (WireBuffer, 1);
+
+  wire_buffer->message = message;
+  wire_buffer->buffer = message;
+  wire_buffer->length = MAX_MESSAGE_LENGTH;
+
+  /* util_io_add_watch (app->context, channel,
+                     G_IO_OUT | G_IO_ERR | G_IO_HUP,
+		     (GIOFunc) write_data, wire_buffer); */
+
+  return write_data (channel, 0, wire_buffer);
+}
+
+gboolean
+wire_message_process (GIOChannel   *channel,
+                      GIOCondition  condition,
+		      EndpointApp  *app)
+{
+  WireBuffer *buffer;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->wire_funcs != NULL, FALSE);
+
+  if (condition & G_IO_ERR || condition & G_IO_HUP)
+    return FALSE;
+
+  buffer = g_new0 (WireBuffer, 1);
+
+  buffer->message = g_new (guint8, MAX_MESSAGE_LENGTH);
+  buffer->buffer = buffer->message;
+  buffer->length = MAX_MESSAGE_LENGTH;
+
+  if (!read_data (channel, buffer))
+    return FALSE;
+
+  dispatch_message (app, buffer->message);
+
+  g_free (buffer->message);
+  g_free (buffer);
+
+  return TRUE;
+}
+
+gpointer
+wire_message_copy (gpointer message)
+{
+  return g_memdup (message, MAX_MESSAGE_LENGTH);
+}
+
+gpointer
+wire_message_login (guint         login_ID,
+                    gint          port,
+                    nodeid_t      node,
+		    SBP2Pointer  *pointer,
+		    SBP2LoginORB *orb)
+{
+  WireLogin *message;
+
+  g_return_val_if_fail (pointer != NULL, NULL);
+  g_return_val_if_fail (orb != NULL, NULL);
+
+  message = alloc_message (WIRE_LOGIN);
+  message->login_ID = login_ID;
+
+  message->node = node;
+
+  memcpy (&message->pointer, pointer, sizeof (SBP2Pointer));
+  memcpy (&message->orb, orb, sizeof (SBP2LoginORB));
+
+  return message;
+}
+
+gpointer
+wire_message_reconnect (guint    login_ID,
+                        nodeid_t node)
+{
+  WireReconnect *message;
+
+  message = alloc_message (WIRE_RECONNECT);
+  message->login_ID = login_ID;
+
+  message->node = node;
+
+  return message;
+}
+
+gpointer 
+wire_message_logout (guint login_ID)
+{
+  return alloc_message_login_ID (WIRE_LOGOUT, login_ID);
+}
+
+gpointer 
+wire_message_worker_active (guint login_ID)
+{
+  return alloc_message_login_ID (WIRE_WORKER_ACTIVE, login_ID);
+}
+
+gpointer 
+wire_message_worker_died (guint login_ID)
+{
+  return alloc_message_login_ID (WIRE_WORKER_DIED, login_ID);
+}
+
+static gboolean
+read_data (GIOChannel *channel,
+           WireBuffer *wire_buffer)
+{
+  gchar     *buffer;
+  gsize      length;
+  gsize      bytes;
+  GIOStatus  status;
+  GError    *error = NULL;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (wire_buffer != NULL, FALSE);
+
+  buffer = wire_buffer->buffer;
+  length = wire_buffer->length;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (buffer != NULL, FALSE);
+
+  while (length > 0)
+    {
+      do
+	{
+	  bytes = 0;
+	  status = g_io_channel_read_chars (channel,
+	                                    buffer, length,
+					    &bytes, &error);
+	}
+      while (status == G_IO_STATUS_AGAIN);
+
+      if (status != G_IO_STATUS_NORMAL)
+	{
+	  if (error)
+	    {
+	      g_warning ("wire read data: error: %s", error->message);
+	      g_error_free (error);
+	    }
+	  else
+	    {
+	      g_warning ("wire read data: error");
+	    }
+
+	  return FALSE;
+	}
+
+      if (bytes == 0)
+	{
+	  g_warning ("wire read data: unexpected EOF");
+	  return FALSE;
+	}
+
+      length -= bytes;
+      buffer += bytes;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+write_data (GIOChannel   *channel,
+            GIOCondition  condition,
+	    WireBuffer   *wire_buffer)
+{
+  gsize      bytes;
+  GIOStatus  status;
+  GError    *error = NULL;
+
+  g_return_val_if_fail (channel != NULL, FALSE);
+  g_return_val_if_fail (wire_buffer != NULL, FALSE);
+  g_return_val_if_fail (wire_buffer->message != NULL, FALSE);
+  g_return_val_if_fail (wire_buffer->buffer != NULL, FALSE);
+  g_return_val_if_fail (wire_buffer->length > 0, FALSE);
+
+  if (condition & G_IO_ERR || condition & G_IO_HUP)
+    goto free_and_stop;
+
+  bytes = 0;
+  status = g_io_channel_write_chars (channel,
+				     wire_buffer->buffer, wire_buffer->length,
+				     &bytes, &error);
+
+  if (status == G_IO_STATUS_AGAIN)
+    return TRUE;
+
+  if (status != G_IO_STATUS_NORMAL)
+    {
+      if (error)
+	{
+	  g_warning ("wire write data: error: %s", error->message);
+	  g_error_free (error);
+	}
+      else
+	g_warning ("wire write data: error");
+
+      goto free_and_stop;
+    }
+
+  wire_buffer->length -= bytes;
+  wire_buffer->buffer += bytes;
+
+  if (wire_buffer->length > 0)
+    return TRUE;
+
+free_and_stop:
+  g_free (wire_buffer->message);
+  g_free (wire_buffer);
+
+  return FALSE;
+}
+
+static void
+dispatch_message (EndpointApp *app,
+                  gpointer     message)
+{
+  WireMsgFuncs *funcs;
+
+  g_return_if_fail (app != NULL);
+  g_return_if_fail (app->wire_funcs != NULL);
+  g_return_if_fail (message != NULL);
+
+  funcs = app->wire_funcs;
+
+  switch (get_message_type (message))
+    {
+    case WIRE_LOGIN:
+      g_return_if_fail (funcs->login != NULL);
+
+      funcs->login (app, message);
+      break;
+
+    case WIRE_RECONNECT:
+      g_return_if_fail (funcs->reconnect != NULL);
+
+      funcs->reconnect (app, message);
+      break;
+
+    case WIRE_LOGOUT:
+      g_return_if_fail (funcs->logout != NULL);
+
+      funcs->logout (app, message);
+      break;
+
+    case WIRE_WORKER_ACTIVE:
+      g_return_if_fail (funcs->worker_active != NULL);
+
+      funcs->worker_active (app, message);
+      break;
+
+    case WIRE_WORKER_DIED:
+      g_return_if_fail (funcs->worker_died != NULL);
+
+      funcs->worker_died (app, message);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}

Added: trunk/src/wire.h
==============================================================================
--- trunk/src/wire.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/wire.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,131 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __WIRE_MESSAGES_H__
+#define __WIRE_MESSAGES_H__
+
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <libsbp2/sbp2login.h>
+
+#include "app.h"
+
+
+typedef enum
+{
+  WIRE_LOGIN,
+  WIRE_RECONNECT,
+  WIRE_LOGOUT,
+
+  WIRE_WORKER_ACTIVE,
+  WIRE_WORKER_DIED
+} WireMessageType;
+
+
+typedef struct _WireHeader WireHeader;
+
+typedef struct _WireLogin WireLogin;
+typedef struct _WireReconnect WireReconnect;
+
+typedef struct _WireLoginID WireLogout;
+typedef struct _WireLoginID WireWorkerActive;
+typedef struct _WireLoginID WireWorkerDied;
+
+typedef struct _WireMsgFuncs WireMsgFuncs;
+
+struct _WireHeader
+{
+  WireMessageType type;
+};
+
+struct _WireLogin
+{
+  WireHeader   header;
+
+  guint        login_ID;
+  gint         port;
+
+  nodeid_t     node;
+  SBP2Pointer  pointer;
+  SBP2LoginORB orb;
+};
+
+struct _WireReconnect
+{
+  WireHeader header;
+
+  guint      login_ID;
+  nodeid_t   node;
+};
+
+struct _WireLoginID
+{
+  WireHeader header;
+
+  guint      login_ID;
+};
+
+struct _WireMsgFuncs
+{
+  gboolean (*login)         (EndpointApp      *app,
+			     WireLogin        *message);
+  gboolean (*reconnect)     (EndpointApp      *app,
+			     WireReconnect    *message);
+  gboolean (*logout)        (EndpointApp      *app,
+			     WireLogout       *message);
+
+  gboolean (*worker_active) (EndpointApp      *app,
+			     WireWorkerActive *message);
+  gboolean (*worker_died)   (EndpointApp      *app,
+			     WireWorkerDied   *message);
+};
+
+
+gboolean wire_init                  (void);
+
+gboolean wire_message_send          (GIOChannel   *channel,
+				     gpointer      message,
+				     EndpointApp  *app);
+gboolean wire_message_process       (GIOChannel   *channel,
+				     GIOCondition  condition,
+				     EndpointApp  *app);
+
+gpointer wire_message_copy          (gpointer      message);
+
+gpointer wire_message_login         (guint         login_ID,
+				     gint          port,
+				     nodeid_t      node,
+				     SBP2Pointer  *pointer,
+				     SBP2LoginORB *orb);
+gpointer wire_message_reconnect     (guint         login_ID,
+				     nodeid_t      node);
+gpointer wire_message_logout        (guint         login_ID);
+
+gpointer wire_message_worker_active (guint         login_ID);
+gpointer wire_message_worker_died   (guint         login_ID);
+
+
+#endif /* __WIRE_MESSAGES_H__ */

Added: trunk/src/worker.c
==============================================================================
--- trunk/src/worker.c	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/worker.c	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,129 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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.
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <libraw1394/raw1394.h>
+
+#include <librbc/rbcdisk.h>
+
+#include <libsbp2/sbp2login.h>
+#include <libsbp2/sbp2worker.h>
+
+#include "app.h"
+#include "worker.h"
+
+
+typedef struct _WorkerState WorkerState;
+
+struct _WorkerState
+{
+  EndpointApp *app;
+
+  RBCDisk     *disk;
+  SBP2Worker  *worker;
+
+  GIOChannel  *reader;
+  GIOChannel  *writer;
+};
+
+
+static gboolean reconnect (EndpointApp   *app,
+                           WireReconnect *message);
+
+
+static WireMsgFuncs wire_funcs = {
+  .reconnect = reconnect
+};
+
+void
+worker_process (EndpointApp *app,
+                Process     *process,
+                WireLogin   *message)
+{
+  WorkerState      *state;
+  RBCDisk          *disk;
+  SBP2Worker       *worker;
+  WireWorkerActive *ret_message;
+
+  g_return_if_fail (app != NULL);
+  g_return_if_fail (process != NULL);
+
+  disk = g_hash_table_lookup (app->disks, GINT_TO_POINTER (message->orb.lun));
+
+  if (!rbc_disk_activate (disk))
+    exit (1);
+
+  worker = sbp2_worker_new (disk, app->context,
+                            message->port, message->login_ID);
+
+  if (!worker)
+    exit (2);
+
+  if (!sbp2_worker_login (worker, message->node, &message->pointer,
+	                  &message->orb))
+    exit (3);
+
+  app->wire_funcs = &wire_funcs;
+
+  state = g_new (WorkerState, 1);
+
+  state->app = app;
+
+  state->disk = disk;
+  state->worker = worker;
+
+  state->reader = process->reader;
+  state->writer = process->writer;
+
+  app->user_data = state;
+
+  ret_message = wire_message_worker_active (message->login_ID);
+  wire_message_send (state->writer, ret_message, app);
+}
+
+static gboolean
+reconnect (EndpointApp   *app,
+	   WireReconnect *message)
+{
+  WorkerState      *state;
+  WireWorkerActive *ret_message;
+
+  g_return_val_if_fail (app != NULL, FALSE);
+  g_return_val_if_fail (app->user_data != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
+
+  state = app->user_data;
+
+  if (sbp2_worker_reconnect (state->worker, message->node))
+    {
+      ret_message = wire_message_worker_active (message->login_ID);
+      wire_message_send (state->writer, ret_message, app);
+    }
+  else
+    exit (4);
+
+  return TRUE;
+}

Added: trunk/src/worker.h
==============================================================================
--- trunk/src/worker.h	2003-07-17 19:25:32 UTC (rev 1)
+++ trunk/src/worker.h	2003-07-17 19:27:07 UTC (rev 2)
@@ -0,0 +1,38 @@
+/*
+ * Endpoint - Linux SBP2 Disk Target
+ *
+ * Copyright (C) 2003 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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 recieved 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 __WORKER_H__
+#define __WORKER_H__
+
+
+#include "app.h"
+#include "process.h"
+#include "wire.h"
+
+
+void worker_process (EndpointApp *app,
+                     Process     *process,
+		     WireLogin   *message);
+
+
+#endif /* __WORKER_H__ */



More information about the Endpoint-commits mailing list