[packages/open-iscsi] - updated to 2.1.4 + current Fedora patch set - updated systemd patch - removed obsolete git,build p

qboosh qboosh at pld-linux.org
Mon May 16 21:49:45 CEST 2022


commit 3de50fc5e99b5643efe9c051d2892c3800666e04
Author: Jakub Bogusz <qboosh at pld-linux.org>
Date:   Mon May 16 21:50:10 2022 +0200

    - updated to 2.1.4 + current Fedora patch set
    - updated systemd patch
    - removed obsolete git,build patches
    - added libscsi patch, rename Fedora-provided libiscsi to libopeniscsi to resolve conflict with libiscsi.spec

 ...emove-dependences-from-iscsi-init.service.patch |    28 +
 0001-unit-file-tweaks.patch                        |   175 +
 0005-update-initscripts-and-docs.patch             |   134 +
 0008-libiscsi.patch                                |  4014 +++
 0009-Add-macros-to-release-GIL-lock.patch          |    56 +
 0010-libiscsi-introduce-sessions-API.patch         |   290 +
 ...-fix-discovery-request-timeout-regression.patch |    32 +
 0012-libiscsi-format-security-build-errors.patch   |    35 +
 ...libiscsi-fix-build-to-use-libopeniscsiusr.patch |    36 +
 ...i-fix-build-against-latest-upstream-again.patch |    66 +
 0015-remove-the-offload-boot-supported-ifdef.patch |    45 +
 ...rt-iscsiadm-return-error-when-login-fails.patch |    34 +
 0019-Coverity-scan-fixes.patch                     |   100 +
 ...stream-build-breakage-of-iscsiuio-LDFLAGS.patch |    25 +
 ...replace-zero-length-array-with-flexible-a.patch |    44 +
 0023-stop-using-Werror-for-now.patch               |    56 +
 0024-minor-service-file-updates.patch              |    68 +
 ...initrd-option-to-set-run-from-initrd-hint.patch |    61 -
 ...csid-newroot-command-to-survive-switch_ro.patch |   158 -
 ...scsiuio-systemd-socket-activation-support.patch |    58 -
 ...-param-parsing-for-advanced-node-creation.patch |   337 -
 ...emd-service-files-add-iscsi.service-for-s.patch |    93 -
 0050-iscsi-boot-related-service-file-updates.patch |    75 -
 0058-iscsiuio-IPC-newroot-command.patch            |   122 -
 0059-iscsiuio-systemd-unit-files.patch             |    53 -
 ...-for-autostart-sessions-if-iscsi-is-not-u.patch |    30 -
 ...f-setting-uid-gid-and-drop-supplementary-.patch |    68 -
 0065-fix-hardened-build-of-iscsiuio.patch          |    31 -
 ...tart-socket-listeners-on-iscsiadm-command.patch |    28 -
 open-iscsi-build.patch                             |    30 -
 open-iscsi-git.patch                               | 35445 -------------------
 open-iscsi-libiscsi.patch                          |    20 +
 open-iscsi-systemd.patch                           |   110 +-
 open-iscsi.spec                                    |   261 +-
 34 files changed, 5507 insertions(+), 36711 deletions(-)
---
diff --git a/open-iscsi.spec b/open-iscsi.spec
index f0637fc..1bc271d 100644
--- a/open-iscsi.spec
+++ b/open-iscsi.spec
@@ -1,50 +1,58 @@
-# Conditional build:
-%bcond_without	dynamic		# link utilities dynamically
 #
-%define		ver	2.0
-%define		subver	873
+# Conditional build:
+%bcond_without	python2	# CPython 2.x module
+%bcond_without	python3	# CPython 3.x module
+
 Summary:	iSCSI - SCSI over IP
 Summary(pl.UTF-8):	iSCSI - SCSI po IP
 Name:		open-iscsi
-Version:	%{ver}.%{subver}
-Release:	5
+Version:	2.1.4
+Release:	1
 License:	GPL v2
 Group:		Networking/Daemons
-Source0:	http://www.open-iscsi.org/bits/%{name}-%{ver}-%{subver}.tar.gz
-# Source0-md5:	8b8316d7c9469149a6cc6234478347f7
+#Source0Download: https://github.com/open-iscsi/open-iscsi/releases
+Source0:	https://github.com/open-iscsi/open-iscsi/archive/%{version}/%{name}-%{version}.tar.gz
+# Source0-md5:	e17f1924c1d64342773eae630e15c519
 Source1:	%{name}.init
 Source2:	%{name}.sysconfig
 Source3:	%{name}-devices.init
 Source4:	iscsiuio.logrotate
-Patch0:		%{name}-git.patch
-Patch1:		%{name}-build.patch
-Patch2:		%{name}-systemd.patch
-Patch32:	0044-iscsid-add-initrd-option-to-set-run-from-initrd-hint.patch
-Patch35:	0047-iscsiadm-iscsid-newroot-command-to-survive-switch_ro.patch
-Patch36:	0047-iscsiuio-systemd-socket-activation-support.patch
-Patch37:	0048-iscsiadm-param-parsing-for-advanced-node-creation.patch
-Patch38:	0049-update-systemd-service-files-add-iscsi.service-for-s.patch
-Patch39:	0050-iscsi-boot-related-service-file-updates.patch
-Patch40:	0058-iscsiuio-IPC-newroot-command.patch
-Patch41:	0059-iscsiuio-systemd-unit-files.patch
-Patch42:	0062-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch
-Patch43:	0063-fix-order-of-setting-uid-gid-and-drop-supplementary-.patch
-Patch44:	0065-fix-hardened-build-of-iscsiuio.patch
-Patch45:	0066-start-socket-listeners-on-iscsiadm-command.patch
-URL:		http://www.open-iscsi.org/
+# Fedora patches
+Patch1:		0001-unit-file-tweaks.patch
+# idmb_rec_write refactoring skipped, see 75c46b011d7485a4b5676d824c7f3cdea2076f49
+Patch5:		0005-update-initscripts-and-docs.patch
+# use-var-for-config, use-red-hat-for-name skipped
+Patch8:		0008-libiscsi.patch
+Patch9:		0009-Add-macros-to-release-GIL-lock.patch
+Patch10:	0010-libiscsi-introduce-sessions-API.patch
+Patch11:	0011-libiscsi-fix-discovery-request-timeout-regression.patch
+Patch12:	0012-libiscsi-format-security-build-errors.patch
+Patch13:	0013-libiscsi-fix-build-to-use-libopeniscsiusr.patch
+Patch14:	0014-libiscsi-fix-build-against-latest-upstream-again.patch
+Patch15:	0015-remove-the-offload-boot-supported-ifdef.patch
+Patch16:	0016-Revert-iscsiadm-return-error-when-login-fails.patch
+# dont-install-scripts, use-var-lib-iscsi-in-libopeniscsiusr skipped
+Patch19:	0019-Coverity-scan-fixes.patch
+Patch20:	0020-fix-upstream-build-breakage-of-iscsiuio-LDFLAGS.patch
+# use-Red-Hat-version-string-to-match-RPM-package-vers skipped
+Patch22:	0022-iscsi_if.h-replace-zero-length-array-with-flexible-a.patch
+Patch23:	0023-stop-using-Werror-for-now.patch
+Patch24:	0024-minor-service-file-updates.patch
+Patch25:	0001-Remove-dependences-from-iscsi-init.service.patch
+# PLD specific
+Patch100:	%{name}-systemd.patch
+Patch101:	%{name}-libiscsi.patch
+URL:		https://www.open-iscsi.com/
 BuildRequires:	kmod-devel
+BuildRequires:	open-isns-devel
 BuildRequires:	openssl-devel
-BuildRequires:	rpmbuild(macros) >= 1.671
-%if %{with dynamic}
-BuildRequires:	openslp-devel
-BuildRequires:	sed >= 4.0
-Requires:	openslp >= 2.0.0
-%else
-BuildRequires:	glibc-static
-BuildRequires:	openslp-static
-%endif
+%{?with_python2:BuildRequires:	python-devel >= 1:2.5}
+%{?with_python3:BuildRequires:	python3-devel >= 1:3.2}
+BuildRequires:	rpm-pythonprov
+BuildRequires:	rpmbuild(macros) >= 1.714
 Requires(post,preun):	/sbin/chkconfig
 Requires(post,preun,postun):	systemd-units >= 38
+Requires:	%{name}-libs = %{version}-%{release}
 Requires:	rc-scripts
 Requires:	systemd-units >= 38
 Suggests:	multipath-tools
@@ -70,27 +78,74 @@ Protokół iSCSI jest zdefiniowany przez IETF do składowania IP. Więcej
 informacji o protokole iSCSI znajduje się w standardach IETF na
 <http://www.ietf.org/>.
 
+%package libs
+Summary:	Open-iSCSI shared libraries
+Summary(pl.UTF-8):	Biblioteki współdzielone Open-iSCSI
+Group:		Libraries
+
+%description libs
+Open-iSCSI shared libraries.
+
+%description libs -l pl.UTF-8
+Biblioteki współdzielone Open-iSCSI.
+
+%package devel
+Summary:	Header files for Open-iSCSI libraries
+Summary(pl.UTF-8):	Pliki nagłówkowe bibliotek Open-iSCSI
+Group:		Development/Libraries
+Requires:	%{name}-libs = %{version}-%{release}
+
+%description devel
+Header files for Open-iSCSI libraries.
+
+%description devel -l pl.UTF-8
+Pliki nagłówkowe bibliotek Open-iSCSI.
+
+%package -n python-pyiscsi
+Summary:	Python 2 interface to Open-iSCSI library
+Summary(pl.UTF-8):	Interfejs Pythona 2 do biblioteki Open-iSCSI
+Group:		Libraries/Python
+Requires:	%{name}-libs = %{version}-%{release}
+
+%description -n python-pyiscsi
+Python 2 interface to Open-iSCSI library.
+
+%description -n python-pyiscsi -l pl.UTF-8
+Interfejs Pythona 2 do biblioteki Open-iSCSI.
+
+%package -n python3-pyiscsi
+Summary:	Python 3 interface to Open-iSCSI library
+Summary(pl.UTF-8):	Interfejs Pythona 3 do biblioteki Open-iSCSI
+Group:		Libraries/Python
+Requires:	%{name}-libs = %{version}-%{release}
+
+%description -n python3-pyiscsi
+Python 3 interface to Open-iSCSI library.
+
+%description -n python3-pyiscsi -l pl.UTF-8
+Interfejs Pythona 3 do biblioteki Open-iSCSI.
+
 %prep
-%setup -q -n %{name}-%{ver}-%{subver}
-%patch0 -p1
-%patch32 -p1
-%patch35 -p1
-%patch36 -p1
-%patch37 -p1
-%patch38 -p1
-%patch39 -p1
-%patch40 -p1
-%patch41 -p1
-%patch42 -p1
-%patch43 -p1
-%patch44 -p1
-%patch45 -p1
+%setup -q
 %patch1 -p1
-%patch2 -p1
-
-%if %{with dynamic}
-sed -i -e 's/-static //' usr/Makefile
-%endif
+%patch5 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch19 -p1
+%patch20 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch100 -p1
+%patch101 -p1
 
 %build
 cd iscsiuio
@@ -100,47 +155,67 @@ cd iscsiuio
 %{__autoheader}
 %{__automake}
 %configure
+cd ..
 
-cd ../utils/open-isns
-%configure \
-	--with-slp \
-	--without-security
-cd ../..
 %{__make} \
 	CC="%{__cc}" \
-	OPTFLAGS="%{rpmcflags} %{rpmcppflags} -DUSE_KMOD -lkmod" \
-	IPC_FLAGS="-DNETLINK_ISCSI=8 -D_GNU_SOURCE" \
+	OPTFLAGS="%{rpmcflags} %{rpmcppflags}" \
+	SED=sed \
 	KSUBLEVEL=0
 
+cd libiscsi
+%if %{with python2}
+%py_build
+%endif
+%if %{with python3}
+%py3_build
+%endif
+
 %install
 rm -rf $RPM_BUILD_ROOT
 install -d $RPM_BUILD_ROOT%{_sysconfdir}/iscsi/{nodes,send_targets,static,isns,slp,ifaces} \
 	$RPM_BUILD_ROOT/etc/{rc.d/init.d,sysconfig,logrotate.d} \
 	$RPM_BUILD_ROOT%{systemdunitdir} \
-	$RPM_BUILD_ROOT/lib/systemd/pld-helpers.d
+	$RPM_BUILD_ROOT{/sbin,/lib/systemd/pld-helpers.d}
 
-%{__make} install_programs install_doc install_etc \
+%{__make} -j1 install_programs install_doc install_etc install_libopeniscsiusr \
 	DESTDIR=$RPM_BUILD_ROOT
 
 :> $RPM_BUILD_ROOT%{_sysconfdir}/iscsi/initiatorname.iscsi
 
 install %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/iscsid
-install %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iscsi
+cp -p %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iscsi
 install %{SOURCE3} $RPM_BUILD_ROOT/etc/rc.d/init.d/iscsi
-install %{SOURCE4} $RPM_BUILD_ROOT/etc/logrotate.d/iscsiuio
+cp -p %{SOURCE4} $RPM_BUILD_ROOT/etc/logrotate.d/iscsiuio
 
 install usr/iscsistart $RPM_BUILD_ROOT%{_sbindir}
-install doc/iscsistart.8 $RPM_BUILD_ROOT%{_mandir}/man8
-install doc/iscsi-iname.8 $RPM_BUILD_ROOT%{_mandir}/man8
+cp -p doc/iscsistart.8 $RPM_BUILD_ROOT%{_mandir}/man8
+#install doc/iscsi-iname.8 $RPM_BUILD_ROOT%{_mandir}/man8
 
-install etc/systemd/iscsi.service $RPM_BUILD_ROOT%{systemdunitdir}
-install etc/systemd/iscsid.service $RPM_BUILD_ROOT%{systemdunitdir}
-install etc/systemd/iscsid.socket $RPM_BUILD_ROOT%{systemdunitdir}
-install etc/systemd/iscsiuio.service $RPM_BUILD_ROOT%{systemdunitdir}
-install etc/systemd/iscsiuio.socket $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsi.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsi-init.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsi-onboot.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsi-shutdown.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsid.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsid.socket $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsiuio.service $RPM_BUILD_ROOT%{systemdunitdir}
+cp -p etc/systemd/iscsiuio.socket $RPM_BUILD_ROOT%{systemdunitdir}
 
 install etc/systemd/iscsi-mark-root-nodes $RPM_BUILD_ROOT/lib/systemd/pld-helpers.d
 
+# rename to resolve conflict with already existing libiscsi from libiscsi.spec
+install -p libiscsi/libopeniscsi.so.0 $RPM_BUILD_ROOT%{_libdir}
+ln -sf libopeniscsi.so.0 $RPM_BUILD_ROOT%{_libdir}/libopeniscsi.so
+cp -p libiscsi/libiscsi.h $RPM_BUILD_ROOT%{_includedir}/libopeniscsi.h
+
+cd libiscsi
+%if %{with python2}
+%py_install
+%endif
+%if %{with python3}
+%py3_install
+%endif
+
 %clean
 rm -rf $RPM_BUILD_ROOT
 
@@ -155,7 +230,7 @@ fi
 /sbin/chkconfig --add iscsi
 /sbin/chkconfig --add iscsid
 NORESTART=1
-%systemd_post iscsi.service iscsid.service iscsiuio.service iscsid.socket iscsiuio.socket
+%systemd_post iscsi.service iscsid.service iscsiuio.service iscsid.socket iscsiuio.socket iscsi-onboot.service iscsi-init.service iscsi-shutdown.service
 
 %preun
 if [ "$1" = "0" ]; then
@@ -164,7 +239,7 @@ if [ "$1" = "0" ]; then
 	/sbin/chkconfig --del iscsid
 	/sbin/chkconfig --del iscsi
 fi
-%systemd_preun iscsi.service iscsid.service iscsiuio.service iscsid.socket iscsiuio.socket
+%systemd_preun iscsi.service iscsid.service iscsiuio.service iscsid.socket iscsiuio.socket iscsi-onboot.service iscsi-init.service iscsi-shutdown.service
 
 %postun
 if [ "$1" = "0" ]; then
@@ -178,9 +253,12 @@ fi
 /bin/systemctl --quiet enable iscsid.socket || :
 /bin/systemctl --quiet enable iscsiuio.socket || :
 
+%post	libs -p /sbin/ldconfig
+%postun	libs -p /sbin/ldconfig
+
 %files
 %defattr(644,root,root,755)
-%doc Changelog README THANKS
+%doc Changelog README THANKS TODO
 %dir %{_sysconfdir}/iscsi
 %dir %{_sysconfdir}/iscsi/ifaces
 %dir %{_sysconfdir}/iscsi/isns
@@ -195,20 +273,55 @@ fi
 %attr(754,root,root) /etc/rc.d/init.d/iscsi
 %attr(754,root,root) /etc/rc.d/init.d/iscsid
 %{systemdunitdir}/iscsi.service
+%{systemdunitdir}/iscsi-init.service
+%{systemdunitdir}/iscsi-onboot.service
+%{systemdunitdir}/iscsi-shutdown.service
 %{systemdunitdir}/iscsid.service
 %{systemdunitdir}/iscsid.socket
 %{systemdunitdir}/iscsiuio.service
 %{systemdunitdir}/iscsiuio.socket
 %attr(755,root,root) /lib/systemd/pld-helpers.d/iscsi-mark-root-nodes
+%attr(755,root,root) %{_sbindir}/iscsi-gen-initiatorname
 %attr(755,root,root) %{_sbindir}/iscsi-iname
+%attr(755,root,root) %{_sbindir}/iscsi_discovery
+%attr(755,root,root) %{_sbindir}/iscsi_fw_login
+%attr(755,root,root) %{_sbindir}/iscsi_offload
 %attr(755,root,root) %{_sbindir}/iscsiadm
 %attr(755,root,root) %{_sbindir}/iscsid
 %attr(755,root,root) %{_sbindir}/iscsistart
-%attr(755,root,root) %{_sbindir}/iscsi_discovery
 %attr(755,root,root) %{_sbindir}/iscsiuio
 %{_mandir}/man8/iscsi-iname.8*
 %{_mandir}/man8/iscsi_discovery.8*
+%{_mandir}/man8/iscsi_fw_login.8*
 %{_mandir}/man8/iscsiadm.8*
 %{_mandir}/man8/iscsid.8*
 %{_mandir}/man8/iscsistart.8*
 %{_mandir}/man8/iscsiuio.8*
+
+%files libs
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/libopeniscsi.so.0
+%attr(755,root,root) %{_libdir}/libopeniscsiusr.so.*.*.*
+%attr(755,root,root) %ghost %{_libdir}/libopeniscsiusr.so.0
+
+%files devel
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/libopeniscsi.so
+%attr(755,root,root) %{_libdir}/libopeniscsiusr.so
+%{_includedir}/libopeniscsi.h
+%{_includedir}/libopeniscsiusr*.h
+%{_pkgconfigdir}/libopeniscsiusr.pc
+
+%if %{with python2}
+%files -n python-pyiscsi
+%defattr(644,root,root,755)
+%attr(755,root,root) %{py_sitedir}/libiscsi.so
+%{py_sitedir}/PyIscsi-1.0-py*.egg-info
+%endif
+
+%if %{with python3}
+%files -n python3-pyiscsi
+%defattr(644,root,root,755)
+%attr(755,root,root) %{py3_sitedir}/libiscsi.cpython-*.so
+%{py3_sitedir}/PyIscsi-1.0-py*.egg-info
+%endif
diff --git a/0001-Remove-dependences-from-iscsi-init.service.patch b/0001-Remove-dependences-from-iscsi-init.service.patch
new file mode 100644
index 0000000..66c4cc4
--- /dev/null
+++ b/0001-Remove-dependences-from-iscsi-init.service.patch
@@ -0,0 +1,28 @@
+From 432bbf979ee66ee29bb92e35fd6e3ffb948563e3 Mon Sep 17 00:00:00 2001
+From: Lee Duncan <lduncan at suse.com>
+Date: Wed, 29 Sep 2021 11:48:16 -0700
+Subject: [PATCH] Remove dependences from iscsi-init.service
+
+Since iscsid.service depends on it but disables
+default dependencies, iscsi-init.service must
+also disable default dependencies, or a dependency
+loop can be created.
+---
+ etc/systemd/iscsi-init.service | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/etc/systemd/iscsi-init.service b/etc/systemd/iscsi-init.service
+index e058ff0..eab4ff2 100644
+--- a/etc/systemd/iscsi-init.service
++++ b/etc/systemd/iscsi-init.service
+@@ -1,6 +1,7 @@
+ [Unit]
+ Description=One time configuration for iscsi.service
+ ConditionPathExists=!/etc/iscsi/initiatorname.iscsi
++DefaultDependencies=no
+ Before=iscsid.service
+ 
+ [Service]
+-- 
+2.33.0
+
diff --git a/0001-unit-file-tweaks.patch b/0001-unit-file-tweaks.patch
new file mode 100644
index 0000000..743dccd
--- /dev/null
+++ b/0001-unit-file-tweaks.patch
@@ -0,0 +1,175 @@
+From fd538429be486d057b092e3b9c148add2c5ea9c2 Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Tue, 4 Jun 2019 13:23:32 -0700
+Subject: [PATCH 1/1] unit file tweaks
+
+---
+ etc/systemd/iscsi-mark-root-nodes  | 34 ++++++++++++++++++++++++++++++
+ etc/systemd/iscsi-onboot.service   | 15 +++++++++++++
+ etc/systemd/iscsi-shutdown.service | 15 +++++++++++++
+ etc/systemd/iscsi.service          | 16 +++++++-------
+ etc/systemd/iscsid.service         |  3 +--
+ etc/systemd/iscsiuio.service       |  4 +---
+ 6 files changed, 74 insertions(+), 13 deletions(-)
+ create mode 100755 etc/systemd/iscsi-mark-root-nodes
+ create mode 100644 etc/systemd/iscsi-onboot.service
+ create mode 100644 etc/systemd/iscsi-shutdown.service
+
+diff --git a/etc/systemd/iscsi-mark-root-nodes b/etc/systemd/iscsi-mark-root-nodes
+new file mode 100755
+index 0000000..9d48805
+--- /dev/null
++++ b/etc/systemd/iscsi-mark-root-nodes
+@@ -0,0 +1,34 @@
++#!/bin/bash
++
++ISCSIADM=/usr/sbin/iscsiadm
++start_iscsid=0
++start_iscsiuio=0
++
++while read t num p target flash; do
++  # strip tag number from portal, keep "ip:port"
++  portal=${p%,*}
++  transport=${t%:}
++
++  # use session number to find the iface name in use
++  num=${num#[}; num=${num%]}
++  iface=$(iscsiadm -m session -r $num | grep iface.iscsi_ifacename | cut -d= -f2)
++
++  $ISCSIADM -m node -p $portal -T $target -I $iface -o update -n node.startup -v onboot
++
++  start_iscsid=1
++
++  if [ "$transport" = bnx2i ] || [ "$transport" = qedi ]; then
++    start_iscsiuio=1
++  fi
++done < <( $ISCSIADM -m session )
++
++# force iscsid and iscsiuio to start if needed for
++# recovering sessions created in the initrd
++
++if [ "$start_iscsid" -eq 1 ]; then
++  systemctl --no-block start iscsid.service
++fi
++if [ "$start_iscsiuio" -eq 1 ]; then
++  systemctl --no-block start iscsiuio.service
++fi
++
+diff --git a/etc/systemd/iscsi-onboot.service b/etc/systemd/iscsi-onboot.service
+new file mode 100644
+index 0000000..42ced68
+--- /dev/null
++++ b/etc/systemd/iscsi-onboot.service
+@@ -0,0 +1,15 @@
++[Unit]
++Description=Special handling of early boot iSCSI sessions
++Documentation=man:iscsiadm(8) man:iscsid(8)
++DefaultDependencies=no
++RefuseManualStart=true
++Before=iscsi.service
++After=systemd-remount-fs.service
++ConditionDirectoryNotEmpty=/sys/class/iscsi_session
++
++[Service]
++Type=oneshot
++ExecStart=-/usr/libexec/iscsi-mark-root-nodes
++
++[Install]
++WantedBy=sysinit.target
+diff --git a/etc/systemd/iscsi-shutdown.service b/etc/systemd/iscsi-shutdown.service
+new file mode 100644
+index 0000000..caee933
+--- /dev/null
++++ b/etc/systemd/iscsi-shutdown.service
+@@ -0,0 +1,15 @@
++[Unit]
++Description=Logout off all iSCSI sessions on shutdown
++Documentation=man:iscsid(8) man:iscsiadm(8)
++DefaultDependencies=no
++Conflicts=shutdown.target
++After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
++Before=remote-fs-pre.target
++Wants=remote-fs-pre.target
++RefuseManualStop=yes
++
++[Service]
++Type=oneshot
++RemainAfterExit=true
++ExecStart=-/usr/bin/true
++ExecStop=-/usr/sbin/iscsiadm -m node --logoutall=all
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index 5e394b9..175cb2c 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -1,18 +1,18 @@
+ [Unit]
+ Description=Login and scanning of iSCSI devices
+ Documentation=man:iscsiadm(8) man:iscsid(8)
+-Before=remote-fs.target
+-After=network-online.target iscsid.service
+-Requires=iscsid.socket iscsi-init.service
+-Wants=network-online.target
++DefaultDependencies=no
++Before=remote-fs-pre.target
++After=network.target network-online.target iscsid.service iscsiuio.service systemd-remount-fs.service
++Wants=remote-fs-pre.target iscsi-shutdown.service
++ConditionDirectoryNotEmpty=/var/lib/iscsi/nodes
+ 
+ [Service]
+ Type=oneshot
+-ExecStart=/sbin/iscsiadm -m node --loginall=automatic -W
+-ExecStop=/sbin/iscsiadm -m node --logoutall=automatic
+-ExecStop=/sbin/iscsiadm -m node --logoutall=manual
+-SuccessExitStatus=21 15
+ RemainAfterExit=true
++ExecStart=-/usr/sbin/iscsiadm -m node --loginall=automatic
++ExecReload=-/usr/sbin/iscsiadm -m node --loginall=automatic
++SuccessExitStatus=21
+ 
+ [Install]
+ WantedBy=remote-fs.target
+diff --git a/etc/systemd/iscsid.service b/etc/systemd/iscsid.service
+index 3fd7dd3..324c593 100644
+--- a/etc/systemd/iscsid.service
++++ b/etc/systemd/iscsid.service
+@@ -4,13 +4,12 @@ Documentation=man:iscsid(8) man:iscsiuio(8) man:iscsiadm(8)
+ DefaultDependencies=no
+ After=network-online.target iscsiuio.service iscsi-init.service
+ Before=remote-fs-pre.target
+-Wants=remote-fs-pre.target
+ Requires=iscsi-init.service
+ 
+ [Service]
+ Type=notify
+ NotifyAccess=main
+-ExecStart=/sbin/iscsid -f
++ExecStart=/usr/sbin/iscsid -f
+ KillMode=mixed
+ Restart=on-failure
+ 
+diff --git a/etc/systemd/iscsiuio.service b/etc/systemd/iscsiuio.service
+index 923e019..fc0be93 100644
+--- a/etc/systemd/iscsiuio.service
++++ b/etc/systemd/iscsiuio.service
+@@ -2,17 +2,15 @@
+ Description=iSCSI UserSpace I/O driver
+ Documentation=man:iscsiuio(8)
+ DefaultDependencies=no
+-Conflicts=shutdown.target
+ Requires=iscsid.service
+ BindTo=iscsid.service
+ After=network.target
+ Before=remote-fs-pre.target iscsid.service
+-Wants=remote-fs-pre.target
+ 
+ [Service]
+ Type=notify
+ NotifyAccess=main
+-ExecStart=/sbin/iscsiuio -f
++ExecStart=/usr/sbin/iscsiuio -f
+ KillMode=mixed
+ Restart=on-failure
+ 
+-- 
+2.26.3
+
diff --git a/0005-update-initscripts-and-docs.patch b/0005-update-initscripts-and-docs.patch
new file mode 100644
index 0000000..7d487a5
--- /dev/null
+++ b/0005-update-initscripts-and-docs.patch
@@ -0,0 +1,134 @@
+From 97b1242450df25648d203acf7cc297cd46d10e8c Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech at redhat.com>
+Date: Mon, 19 Nov 2012 16:37:13 -0800
+Subject: [PATCH] update initscripts and docs
+
+---
+ README          | 12 +++++-------
+ etc/iscsid.conf | 23 +++++++++++------------
+ usr/idbm.c      |  4 ++++
+ 3 files changed, 20 insertions(+), 19 deletions(-)
+
+diff --git a/README b/README
+index 508c9d7..b62a14e 100644
+--- a/README
++++ b/README
+@@ -77,11 +77,6 @@ the cache sync command will fail.
+ - iscsiadm's -P 3 option will not print out scsi devices.
+ - iscsid will not automatically online devices.
+ 
+-You need to enable "Cryptographic API" under "Cryptographic options" in the
+-kernel config. And you must enable "CRC32c CRC algorithm" even if
+-you do not use header or data digests. They are the kernel options
+-CONFIG_CRYPTO and CONFIG_CRYPTO_CRC32C, respectively.
+-
+ The userspace components iscsid, iscsiadm and iscsistart require the
+ open-isns library, which can be found here:
+ 	https://github.com/gonzoleeman/open-isns/releases
+@@ -1163,11 +1158,11 @@ Red Hat or Fedora:
+ -----------------
+ To start open-iscsi in Red Hat/Fedora you can do:
+ 
+-	systemctl start open-iscsi
++	systemctl start iscsi
+ 
+ To get open-iscsi to automatically start at run time you may have to
+ run:
+-	systemctl enable open-iscsi
++	systemctl enable iscsi
+ 
+ And, to automatically mount a file system during startup
+ you must have the partition entry in /etc/fstab marked with the "_netdev"
+@@ -1370,6 +1365,9 @@ iscsid will only perform rediscovery when it gets a SCN from the server.
+ #   linux-isns (SLES's iSNS server) where it sometimes does not send SCN
+ #   events in the proper format, so they may not get handled.
+ 
++To set the startup value, so that nodes are not logged into automatically
++use the value "manual".
++
+ Examples
+ --------
+ 
+diff --git a/etc/iscsid.conf b/etc/iscsid.conf
+index f21ed3d..420145b 100644
+--- a/etc/iscsid.conf
++++ b/etc/iscsid.conf
+@@ -19,8 +19,8 @@
+ # the time then leave this attribute commented out.
+ #
+ # Default for Fedora and RHEL. (uncomment to activate).
+-# iscsid.startup = /bin/systemctl start iscsid.socket iscsiuio.socket
+-#
++iscsid.startup = /bin/systemctl start iscsid.socket iscsiuio.socket
++# 
+ # Default if you are not using systemd (uncomment to activate)
+ # iscsid.startup = /usr/bin/service start iscsid
+ 
+@@ -41,8 +41,8 @@
+ # To request that the iscsi initd scripts startup a session set to "automatic".
+ # node.startup = automatic
+ #
+-# To manually startup the session set to "manual". The default is manual.
+-node.startup = manual
++# To manually startup the session set to "manual". The default is automatic.
++node.startup = automatic
+ 
+ # For "automatic" startup nodes, setting this to "Yes" will try logins on each
+ # available iface until one succeeds, and then stop.  The default "No" will try
+@@ -271,28 +271,27 @@ node.conn[0].iscsi.MaxXmitDataSegmentLength = 0
+ discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 32768
+ 
+ # To allow the targets to control the setting of the digest checking,
+-# with the initiator requesting a preference of enabling the checking, uncomment# one or both of the following lines:
++# with the initiator requesting a preference of enabling the checking, uncomment
++# the following lines (Data digests are not supported.):
+ #node.conn[0].iscsi.HeaderDigest = CRC32C,None
+-#node.conn[0].iscsi.DataDigest = CRC32C,None
++
+ #
+ # To allow the targets to control the setting of the digest checking,
+ # with the initiator requesting a preference of disabling the checking,
+-# uncomment one or both of the following lines:
++# uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = None,CRC32C
+-#node.conn[0].iscsi.DataDigest = None,CRC32C
+ #
+ # To enable CRC32C digest checking for the header and/or data part of
+-# iSCSI PDUs, uncomment one or both of the following lines:
++# iSCSI PDUs, uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = CRC32C
+-#node.conn[0].iscsi.DataDigest = CRC32C
+ #
+ # To disable digest checking for the header and/or data part of
+-# iSCSI PDUs, uncomment one or both of the following lines:
++# iSCSI PDUs, uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = None
+-#node.conn[0].iscsi.DataDigest = None
+ #
+ # The default is to never use DataDigests or HeaderDigests.
+ #
++node.conn[0].iscsi.HeaderDigest = None
+ 
+ # For multipath configurations, you may want more than one session to be
+ # created on each iface record.  If node.session.nr_sessions is greater
+diff --git a/usr/idbm.c b/usr/idbm.c
+index f1e5c88..0f0f17a 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -566,9 +566,13 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
+ 				 IDBM_SHOW, "None", "CRC32C", "CRC32C,None",
+ 				 "None,CRC32C", num, 1);
+ 		sprintf(key, CONN_DATA_DIGEST, i);
++
++#if 0
++We do not support data digests
+ 		__recinfo_int_o4(key, ri, r, conn[i].iscsi.DataDigest, IDBM_SHOW,
+ 				 "None", "CRC32C", "CRC32C,None",
+ 				 "None,CRC32C", num, 1);
++#endif
+ 		sprintf(key, CONN_IFMARKER, i);
+ 		__recinfo_int_o2(key, ri, r, conn[i].iscsi.IFMarker, IDBM_SHOW,
+ 				"No", "Yes", num, 1);
+-- 
+2.26.2
+
diff --git a/0008-libiscsi.patch b/0008-libiscsi.patch
new file mode 100644
index 0000000..39d1a84
--- /dev/null
+++ b/0008-libiscsi.patch
@@ -0,0 +1,4014 @@
+From 8b4da8007ef59bbc833fed882ddae57bbcd51f1c Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Mon, 26 Jan 2015 12:57:11 -0800
+Subject: [PATCH] libiscsi
+
+---
+ Makefile                                    |    2 +
+ libiscsi/Makefile                           |   65 +
+ libiscsi/libiscsi.c                         |  617 ++++++++
+ libiscsi/libiscsi.doxy                      | 1473 +++++++++++++++++++
+ libiscsi/libiscsi.h                         |  344 +++++
+ libiscsi/no_date_footer.html                |    6 +
+ libiscsi/pylibiscsi.c                       |  709 +++++++++
+ libiscsi/setup.py                           |    9 +
+ libiscsi/tests/test_discovery_firmware.c    |   53 +
+ libiscsi/tests/test_discovery_sendtargets.c |   60 +
+ libiscsi/tests/test_get_auth.c              |   70 +
+ libiscsi/tests/test_get_initiator_name.c    |   38 +
+ libiscsi/tests/test_get_network_config.c    |   45 +
+ libiscsi/tests/test_login.c                 |   52 +
+ libiscsi/tests/test_logout.c                |   51 +
+ libiscsi/tests/test_params.c                |  103 ++
+ libiscsi/tests/test_set_auth.c              |   58 +
+ usr/Makefile                                |    2 +-
+ usr/discovery.c                             |    5 +
+ usr/idbm.c                                  |    6 +-
+ usr/idbm.h                                  |    3 +
+ usr/iscsi_ipc.h                             |    2 +
+ 22 files changed, 3769 insertions(+), 4 deletions(-)
+ create mode 100644 libiscsi/Makefile
+ create mode 100644 libiscsi/libiscsi.c
+ create mode 100644 libiscsi/libiscsi.doxy
+ create mode 100644 libiscsi/libiscsi.h
+ create mode 100644 libiscsi/no_date_footer.html
+ create mode 100644 libiscsi/pylibiscsi.c
+ create mode 100644 libiscsi/setup.py
+ create mode 100644 libiscsi/tests/test_discovery_firmware.c
+ create mode 100644 libiscsi/tests/test_discovery_sendtargets.c
+ create mode 100644 libiscsi/tests/test_get_auth.c
+ create mode 100644 libiscsi/tests/test_get_initiator_name.c
+ create mode 100644 libiscsi/tests/test_get_network_config.c
+ create mode 100644 libiscsi/tests/test_login.c
+ create mode 100644 libiscsi/tests/test_logout.c
+ create mode 100644 libiscsi/tests/test_params.c
+ create mode 100644 libiscsi/tests/test_set_auth.c
+
+diff --git a/Makefile b/Makefile
+index 7b445a5..4ab091f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -65,6 +65,7 @@ user: iscsiuio/Makefile
+ 	$(MAKE) -C usr
+ 	$(MAKE) -C utils
+ 	$(MAKE) -C iscsiuio
++	$(MAKE) -C libiscsi
+ 	@echo
+ 	@echo "Compilation complete                 Output file"
+ 	@echo "-----------------------------------  ----------------"
+@@ -85,6 +86,7 @@ iscsiuio/configure iscsiuio/Makefile.in: iscsiuio/configure.ac iscsiuio/Makefile
+ force: ;
+ 
+ clean:
++	$(MAKE) -C libiscsi clean
+ 	$(MAKE) -C utils/sysdeps clean
+ 	$(MAKE) -C utils/fwparam_ibft clean
+ 	$(MAKE) -C utils clean
+diff --git a/libiscsi/Makefile b/libiscsi/Makefile
+new file mode 100644
+index 0000000..53f9746
+--- /dev/null
++++ b/libiscsi/Makefile
+@@ -0,0 +1,65 @@
++# This Makefile will work only with GNU make.
++
++ifeq ($(TOPDIR),)
++	TOPDIR = ..
++endif
++
++OSNAME=$(shell uname -s)
++OPTFLAGS ?= -O2 -g
++WARNFLAGS ?= -Wall -Wstrict-prototypes
++CFLAGS = $(OPTFLAGS) $(WARNFLAGS) -I../include -I../usr \
++		-D$(OSNAME) -fPIC -D_GNU_SOURCE -fvisibility=hidden
++LIB = libiscsi.so.0
++TESTS = tests/test_discovery_sendtargets tests/test_discovery_firmware
++TESTS += tests/test_login tests/test_logout tests/test_params
++TESTS += tests/test_get_network_config tests/test_get_initiator_name
++TESTS += tests/test_set_auth tests/test_get_auth
++
++COMMON_SRCS = sysdeps.o
++# sources shared between iscsid, iscsiadm and iscsistart
++ISCSI_LIB_SRCS = netlink.o transport.o cxgbi.o be2iscsi.o iscsi_timer.o initiator_common.o iscsi_err.o session_info.o iscsi_util.o io.o auth.o discovery.o login.o log.o md5.o sha1.o iface.o idbm.o sysfs.o iscsi_sysfs.o iscsi_net_util.o iscsid_req.o iser.o uip_mgmt_ipc.o
++FW_PARAM_SRCS = fw_entry.o prom_lex.o prom_parse.tab.o fwparam_ppc.o fwparam_sysfs.o
++
++# sources shared with the userspace utils, note we build these separately
++# to get PIC versions.
++COMMON_OBJS = $(patsubst %.o, common-objs/%.o, $(COMMON_SRCS))
++USR_OBJS = $(patsubst %.o, usr-objs/%.o, $(ISCSI_LIB_SRCS) strings.o)
++FW_OBJS = $(patsubst %.o, fw-objs/%.o, $(FW_PARAM_SRCS))
++
++# Flags for the tests
++tests/% : CFLAGS = $(OPTFLAGS) $(WARNFLAGS) -I.
++
++all: lib tests html
++
++lib: $(LIB)
++tests: $(TESTS)
++
++common-objs/%.o: ../utils/sysdeps/%.c
++	mkdir -p common-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++usr-objs/%.o: ../usr/%.c
++	mkdir -p usr-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++fw-objs/%.o: ../utils/fwparam_ibft/%.c
++	mkdir -p fw-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++$(LIB): $(COMMON_OBJS) $(FW_OBJS) $(USR_OBJS) libiscsi.o
++	$(CC) $(CFLAGS) -shared -Wl,-soname,$(LIB) $^ -o $@ -L$(TOPDIR)/libopeniscsiusr -lopeniscsiusr
++	ln -s -f $(LIB) libiscsi.so
++
++$(TESTS): $(FW_OBJS) $(COMMON_OBJS) $(USR_OBJS) $(LIB)
++
++html: libiscsi.h libiscsi.doxy
++	doxygen libiscsi.doxy
++
++clean:
++	rm -rf *.o common-objs usr-objs fw-objs libuip-objs libiscsi.so* \
++			.depend *~ html $(TESTS) tests/*~
++
++depend:
++	gcc $(CFLAGS) -M `ls *.c` > .depend
++
++-include .depend ../usr/.depend
+diff --git a/libiscsi/libiscsi.c b/libiscsi/libiscsi.c
+new file mode 100644
+index 0000000..064e4b5
+--- /dev/null
++++ b/libiscsi/libiscsi.c
+@@ -0,0 +1,617 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede at redhat.com>
++ * maintained by open-iscsi at googlegroups.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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <sys/syslog.h>
++#include "libiscsi.h"
++#include "idbm.h"
++#include "discovery.h"
++#include "log.h"
++#include "sysfs.h"
++#include "iscsi_sysfs.h"
++#include "session_info.h"
++#include "iscsi_util.h"
++#include "sysdeps.h"
++#include "iface.h"
++#include "iscsi_proto.h"
++#include "fw_context.h"
++#include "iscsid_req.h"
++#include "iscsi_err.h"
++
++#define CHECK(a) { context->error_str[0] = 0; rc = a; if (rc) goto leave; }
++
++/* UGLY, not thread safe :( */
++static int sysfs_initialized = 0;
++
++struct libiscsi_context {
++	char error_str[256];
++	/* For get_parameter_helper() */
++	const char *parameter;
++	char *value;
++};
++
++static void libiscsi_log(int prio, void *priv, const char *fmt, va_list ap)
++{
++	struct libiscsi_context *context = priv;
++
++	if (prio > LOG_ERR) /* We are only interested in errors (or worse) */
++		return;
++
++	vsnprintf(context->error_str, sizeof(context->error_str), fmt, ap);
++}
++
++struct libiscsi_context *libiscsi_init(void)
++{
++	struct libiscsi_context *context;
++
++	context = calloc(1, sizeof *context);
++	if (!context)
++		return NULL;
++
++	log_init("libiscsi", 1024, libiscsi_log, context);
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++	increase_max_files();
++	if (idbm_init(NULL)) {
++		sysfs_cleanup();
++		free(context);
++		return NULL;
++	}
++
++	iface_setup_host_bindings();
++
++	return context;
++}
++
++void libiscsi_cleanup(struct libiscsi_context *context)
++{
++	idbm_terminate();
++	free_transports();
++	sysfs_cleanup();
++	free(context);
++}
++
++static void free_iface_list(struct list_head *ifaces)
++{
++	struct iface_rec *iface, *tmp_iface;
++
++	list_for_each_entry_safe(iface, tmp_iface, ifaces, list) {
++		list_del(&iface->list);
++		free(iface);
++	}
++}
++
++static void free_rec_list(struct list_head *rec_list)
++{
++	struct node_rec *rec, *tmp;
++	
++	list_for_each_entry_safe(rec, tmp, rec_list, list) {
++		list_del(&rec->list);
++		free(rec);
++	}
++}
++
++int libiscsi_discover_sendtargets(struct libiscsi_context *context,
++	const char *address, int port,
++	const struct libiscsi_auth_info *auth_info,
++	int *nr_found, struct libiscsi_node **found_nodes)
++{
++	struct discovery_rec drec;
++	LIST_HEAD(bound_rec_list);
++	struct node_rec *rec;
++	int rc = 0, found = 0;
++
++	INIT_LIST_HEAD(&bound_rec_list);
++
++	if (nr_found)
++		*nr_found = 0;
++	if (found_nodes)
++		*found_nodes = NULL;
++
++	CHECK(libiscsi_verify_auth_info(context, auth_info))
++
++	/* Fill the drec struct with all needed info */
++	memset(&drec, 0, sizeof drec);
++	idbm_sendtargets_defaults(&drec.u.sendtargets);
++	drec.type = DISCOVERY_TYPE_SENDTARGETS;
++	strlcpy(drec.address, address, sizeof(drec.address));
++	drec.port = port ? port : ISCSI_LISTEN_PORT;
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_chap:
++		drec.u.sendtargets.auth.authmethod = AUTH_METHOD_CHAP;
++		strlcpy(drec.u.sendtargets.auth.username,
++			auth_info->chap.username, AUTH_STR_MAX_LEN);
++		strlcpy((char *)drec.u.sendtargets.auth.password,
++			auth_info->chap.password, AUTH_STR_MAX_LEN);
++		drec.u.sendtargets.auth.password_length =
++			strlen((char *)drec.u.sendtargets.auth.password);
++		strlcpy(drec.u.sendtargets.auth.username_in,
++			auth_info->chap.reverse_username, AUTH_STR_MAX_LEN);
++		strlcpy((char *)drec.u.sendtargets.auth.password_in,
++			auth_info->chap.reverse_password, AUTH_STR_MAX_LEN);
++		drec.u.sendtargets.auth.password_in_length =
++			strlen((char *)drec.u.sendtargets.auth.password_in);
++		break;
++	}
++
++	CHECK(idbm_add_discovery(&drec))
++
++	CHECK(idbm_bind_ifaces_to_nodes(discovery_sendtargets,
++					&drec, NULL, &bound_rec_list))
++
++	/* now add/update records */
++	list_for_each_entry(rec, &bound_rec_list, list) {
++		CHECK(idbm_add_node(rec, &drec, 1 /* overwrite */))
++		found++;
++	}
++
++	if (nr_found)
++		*nr_found = found;
++
++	if (found_nodes && found) {
++		*found_nodes = calloc(found, sizeof **found_nodes);
++		if (*found_nodes == NULL) {
++			snprintf(context->error_str,
++				 sizeof(context->error_str), strerror(ENOMEM));
++			rc = ENOMEM;
++			goto leave;
++		}
++		found = 0;
++		list_for_each_entry(rec, &bound_rec_list, list) {
++			strlcpy((*found_nodes)[found].name, rec->name,
++				 LIBISCSI_VALUE_MAXLEN);
++			(*found_nodes)[found].tpgt = rec->tpgt;
++			strlcpy((*found_nodes)[found].address,
++				 rec->conn[0].address, NI_MAXHOST);
++			(*found_nodes)[found].port = rec->conn[0].port;
++			strlcpy((*found_nodes)[found].iface,
++				 rec->iface.name, LIBISCSI_VALUE_MAXLEN);
++			found++;
++		}
++	}
++
++leave:
++	free_rec_list(&bound_rec_list);
++	return rc;
++}
++
++int libiscsi_discover_firmware(struct libiscsi_context *context,
++	int *nr_found, struct libiscsi_node **found_nodes)
++{
++	struct list_head targets, ifaces, rec_list;
++	discovery_rec_t drec;
++	int rc = 0;
++
++	INIT_LIST_HEAD(&targets);
++	INIT_LIST_HEAD(&ifaces);
++	INIT_LIST_HEAD(&rec_list);
++
++	if (nr_found) {
++		*nr_found = 0;
++	}
++
++	if (found_nodes) {
++		*found_nodes = NULL;
++	}
++
++	rc = fw_get_targets(&targets);
++	if (rc) {
++		log_error("%s: Could not get list of targets from firmware "
++			  "(err %d).\n", __func__, rc);
++		return rc;
++	}
++
++	CHECK(iface_create_ifaces_from_boot_contexts(&ifaces, &targets));
++
++	memset(&drec, 0, sizeof(drec));
++	drec.type = DISCOVERY_TYPE_FW;
++	rc = idbm_bind_ifaces_to_nodes(discovery_fw, &drec, &ifaces, &rec_list);
++	if (rc) {
++		log_error("%s: Could not determine target nodes from firmware "
++			  "(err %d).\n", __func__, rc);
++		goto leave;
++	}
++
++	int node_count = 0;
++	struct list_head *pos;
++	list_for_each(pos, &rec_list) {
++		++node_count;
++	}
++
++	struct libiscsi_node* new_nodes;
++	/* allocate enough space for all the nodes */
++	new_nodes = calloc(node_count, sizeof *new_nodes);
++	if (new_nodes == NULL) {
++		rc = ENOMEM;
++		log_error("%s: %s.\n", __func__, strerror(ENOMEM));
++		goto leave;
++	}
++
++	struct node_rec *rec;
++	struct libiscsi_node *new_node = new_nodes;
++	/* in one loop, add nodes to idbm and create libiscsi_node entries */
++	list_for_each_entry(rec, &rec_list, list) {
++		CHECK(idbm_add_node(rec, NULL, 1 /* overwrite */));
++
++		strlcpy(new_node->name, rec->name, LIBISCSI_VALUE_MAXLEN);
++		new_node->tpgt = rec->tpgt;
++		strlcpy(new_node->address, rec->conn[0].address, NI_MAXHOST);
++		new_node->port = rec->conn[0].port;
++		strlcpy(new_node->iface, rec->iface.name, LIBISCSI_VALUE_MAXLEN);
++
++		++new_node;
++	}
++
++	/* update output parameters */
++	if (nr_found) {
++		*nr_found = node_count;
++	}
++	if (found_nodes) {
++		*found_nodes = new_nodes;
++	}
++
++leave:
++	fw_free_targets(&targets);
++
++	free_iface_list(&ifaces);
++	free_rec_list(&rec_list);
++
++	return rc;
++}
++
++int libiscsi_verify_auth_info(struct libiscsi_context *context,
++	const struct libiscsi_auth_info *auth_info)
++{
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_none:
++		break;
++	case libiscsi_auth_chap:
++		if (!auth_info->chap.username[0]) {
++			strcpy(context->error_str, "Empty username");
++			return EINVAL;
++		}
++		if (!auth_info->chap.password[0]) {
++			strcpy(context->error_str, "Empty password");
++			return EINVAL;
++		}
++		if (auth_info->chap.reverse_username[0] &&
++		    !auth_info->chap.reverse_password[0]) {
++			strcpy(context->error_str, "Empty reverse password");
++		    	return EINVAL;
++		}
++		break;
++	default:
++		sprintf(context->error_str,
++			"Invalid authentication method: %d",
++			(int)auth_info->method);
++		return EINVAL;
++	}
++	return 0;
++}
++
++int libiscsi_node_set_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const struct libiscsi_auth_info *auth_info)
++{
++	int rc = 0;
++
++	CHECK(libiscsi_verify_auth_info(context, auth_info))
++
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_none:
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.authmethod", "None"))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username_in", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password_in", ""))
++		break;
++
++	case libiscsi_auth_chap:
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.authmethod", "CHAP"))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username",
++			auth_info->chap.username))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password",
++			auth_info->chap.password))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username_in",
++			auth_info->chap.reverse_username))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password_in",
++			auth_info->chap.reverse_password))
++		break;
++	}
++leave:
++	return rc;
++}
++
++int libiscsi_node_get_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    struct libiscsi_auth_info *auth_info)
++{
++	int rc = 0;
++	char value[LIBISCSI_VALUE_MAXLEN];
++
++	memset(auth_info, 0, sizeof *auth_info);
++
++	CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.authmethod", value))
++
++	if (!strcmp(value, "None")) {
++		auth_info->method = libiscsi_auth_none;
++	} else if (!strcmp(value, "CHAP")) {
++		auth_info->method = libiscsi_auth_chap;
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.username",
++			auth_info->chap.username))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.password",
++			auth_info->chap.password))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.username_in",
++			auth_info->chap.reverse_username))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.password_in",
++			auth_info->chap.reverse_password))
++	} else {
++		snprintf(context->error_str, sizeof(context->error_str),
++			 "unknown authentication method: %s", value);
++		rc = EINVAL;
++	}
++leave:
++	return rc;
++}
++
++static void node_to_rec(const struct libiscsi_node *node,
++	struct node_rec *rec)
++{
++	memset(rec, 0, sizeof *rec);
++	idbm_node_setup_defaults(rec);
++	strlcpy(rec->name, node->name, TARGET_NAME_MAXLEN);
++	rec->tpgt = node->tpgt;
++	strlcpy(rec->conn[0].address, node->address, NI_MAXHOST);
++	rec->conn[0].port = node->port;
++}
++
++int login_helper(void *data, node_rec_t *rec)
++{
++	char *iface = (char*)data;
++	if (strcmp(iface, rec->iface.name))
++		/* different iface, skip it */
++		return -1;
++
++	int rc = iscsid_req_by_rec(MGMT_IPC_SESSION_LOGIN, rec);
++	if (rc) {
++		iscsi_err_print_msg(rc);
++		rc = ENOTCONN;
++	}
++	return rc;
++}
++
++int libiscsi_node_login(struct libiscsi_context *context,
++	const struct libiscsi_node *node)
++{
++	int nr_found = 0, rc;
++
++	CHECK(idbm_for_each_iface(&nr_found, (void*)node->iface, login_helper,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++static int logout_helper(void *data, struct session_info *info)
++{
++	int rc;
++	struct node_rec *rec = data;
++
++	if (!iscsi_match_session(rec, info))
++		/* Tell iscsi_sysfs_for_each_session this session was not a
++		   match so that it will not increase nr_found. */
++		return -1;
++
++	rc = iscsid_req_by_sid(MGMT_IPC_SESSION_LOGOUT, info->sid);
++	if (rc) {
++		iscsi_err_print_msg(rc);
++		rc = EIO;
++	}
++
++	return rc;
++}
++
++int libiscsi_node_logout(struct libiscsi_context *context,
++	const struct libiscsi_node *node)
++{
++	int nr_found = 0, rc;
++	struct node_rec rec;
++
++	node_to_rec(node, &rec);
++	CHECK(iscsi_sysfs_for_each_session(&rec, &nr_found, logout_helper,0))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No matching session");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++int libiscsi_node_set_parameter(struct libiscsi_context *context,
++	const struct libiscsi_node *node,
++	const char *parameter, const char *value)
++{
++	int nr_found = 0, rc;
++	struct user_param *param;
++	struct list_head params;
++
++	INIT_LIST_HEAD(&params);
++	param = idbm_alloc_user_param(parameter, value);
++	if (!param) {
++		rc = ENOMEM;
++		goto leave;
++	}
++	list_add_tail(&params, &param->list);
++
++	CHECK(idbm_for_each_iface(&nr_found, &params, idbm_node_set_param,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++	free(param->name);
++	free(param);
++leave:
++	return rc;
++}
++
++static int get_parameter_helper(void *data, node_rec_t *rec)
++{
++	struct libiscsi_context *context = data;
++	recinfo_t *info;
++	int i;
++
++	info = idbm_recinfo_alloc(MAX_KEYS);
++	if (!info) {
++		snprintf(context->error_str, sizeof(context->error_str),
++			 strerror(ENOMEM));
++		return ENOMEM;
++	}
++
++	idbm_recinfo_node(rec, info);
++
++	for (i = 0; i < MAX_KEYS; i++) {
++		if (!info[i].visible)
++			continue;
++
++		if (strcmp(context->parameter, info[i].name))
++			continue;
++
++		strlcpy(context->value, info[i].value, LIBISCSI_VALUE_MAXLEN);
++		break;
++	}
++
++	free(info);
++
++	if (i == MAX_KEYS) {
++		strcpy(context->error_str, "No such parameter");
++		return EINVAL;
++	}
++
++	return 0;
++}
++
++int libiscsi_node_get_parameter(struct libiscsi_context *context,
++	const struct libiscsi_node *node, const char *parameter, char *value)
++{
++	int nr_found = 0, rc = 0;
++
++	context->parameter = parameter;
++	context->value = value;
++
++	/* Note we assume there is only one interface, if not we will get
++	   the value from the last interface iterated over!
++	   This (multiple interfaces) can only happen if someone explicitly
++	   created ones using iscsiadm. Even then this should not be a problem
++	   as most settings should be the same independent of the iface. */
++	CHECK(idbm_for_each_iface(&nr_found, context, get_parameter_helper,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++const char *libiscsi_get_error_string(struct libiscsi_context *context)
++{
++	/* Sometimes the core open-iscsi code does not give us an error
++	   message */
++	if (!context->error_str[0])
++		return "Unknown error";
++
++	return context->error_str;
++}
++
++
++/************************** Utility functions *******************************/
++
++int libiscsi_get_firmware_network_config(
++    struct libiscsi_network_config *config)
++{
++	struct boot_context fw_entry;
++
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++
++	memset(config, 0, sizeof *config);
++	memset(&fw_entry, 0, sizeof fw_entry);
++	if (fw_get_entry(&fw_entry))
++		return ENODEV;
++
++	config->dhcp = strlen(fw_entry.dhcp) ? 1 : 0;
++	strlcpy(config->iface_name, fw_entry.iface, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->mac_address, fw_entry.mac, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->ip_address, fw_entry.ipaddr, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->netmask, fw_entry.mask, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->gateway, fw_entry.gateway, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->primary_dns, fw_entry.primary_dns, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->secondary_dns, fw_entry.secondary_dns, LIBISCSI_VALUE_MAXLEN);
++	return 0;
++}
++
++int libiscsi_get_firmware_initiator_name(char *initiatorname)
++{
++	struct boot_context fw_entry;
++
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++
++	memset(initiatorname, 0, LIBISCSI_VALUE_MAXLEN);
++	memset(&fw_entry, 0, sizeof fw_entry);
++	if (fw_get_entry(&fw_entry))
++		return ENODEV;
++
++	strlcpy(initiatorname, fw_entry.initiatorname, LIBISCSI_VALUE_MAXLEN);
++
++	return 0;
++}
+diff --git a/libiscsi/libiscsi.doxy b/libiscsi/libiscsi.doxy
+new file mode 100644
+index 0000000..7a5ff7f
+--- /dev/null
++++ b/libiscsi/libiscsi.doxy
+@@ -0,0 +1,1473 @@
++# Doxyfile 1.5.7.1
++
++# This file describes the settings to be used by the documentation system
++# doxygen (www.doxygen.org) for a project
++#
++# All text after a hash (#) is considered a comment and will be ignored
++# The format is:
++#       TAG = value [value, ...]
++# For lists items can also be appended using:
++#       TAG += value [value, ...]
++# Values that contain spaces should be placed between quotes (" ")
++
++#---------------------------------------------------------------------------
++# Project related configuration options
++#---------------------------------------------------------------------------
++
++# This tag specifies the encoding used for all characters in the config file 
++# that follow. The default is UTF-8 which is also the encoding used for all 
++# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
++# iconv built into libc) for the transcoding. See 
++# http://www.gnu.org/software/libiconv for the list of possible encodings.
++
++DOXYFILE_ENCODING      = UTF-8
++
++# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
++# by quotes) that should identify the project.
++
++PROJECT_NAME           = libiscsi
++
++# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
++# This could be handy for archiving the generated documentation or 
++# if some version control system is used.
++
++PROJECT_NUMBER         = 
++
++# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
++# base path where the generated documentation will be put. 
++# If a relative path is entered, it will be relative to the location 
++# where doxygen was started. If left blank the current directory will be used.
++
++OUTPUT_DIRECTORY       = 
++
++# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
++# 4096 sub-directories (in 2 levels) under the output directory of each output 
++# format and will distribute the generated files over these directories. 
++# Enabling this option can be useful when feeding doxygen a huge amount of 
++# source files, where putting all generated files in the same directory would 
++# otherwise cause performance problems for the file system.
++
++CREATE_SUBDIRS         = NO
++
++# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
++# documentation generated by doxygen is written. Doxygen will use this 
++# information to generate all constant output in the proper language. 
++# The default language is English, other supported languages are: 
++# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
++# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
++# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
++# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
++# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
++# Spanish, Swedish, and Ukrainian.
++
++OUTPUT_LANGUAGE        = English
++
++# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
++# include brief member descriptions after the members that are listed in 
++# the file and class documentation (similar to JavaDoc). 
++# Set to NO to disable this.
++
++BRIEF_MEMBER_DESC      = YES
++
++# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
++# the brief description of a member or function before the detailed description. 
++# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
++# brief descriptions will be completely suppressed.
++
++REPEAT_BRIEF           = NO
++
++# This tag implements a quasi-intelligent brief description abbreviator 
++# that is used to form the text in various listings. Each string 
++# in this list, if found as the leading text of the brief description, will be 
++# stripped from the text and the result after processing the whole list, is 
++# used as the annotated text. Otherwise, the brief description is used as-is. 
++# If left blank, the following values are used ("$name" is automatically 
++# replaced with the name of the entity): "The $name class" "The $name widget" 
++# "The $name file" "is" "provides" "specifies" "contains" 
++# "represents" "a" "an" "the"
++
++ABBREVIATE_BRIEF       = 
++
++# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
++# Doxygen will generate a detailed section even if there is only a brief 
++# description.
++
++ALWAYS_DETAILED_SEC    = YES
++
++# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
++# inherited members of a class in the documentation of that class as if those 
++# members were ordinary class members. Constructors, destructors and assignment 
++# operators of the base classes will not be shown.
++
++INLINE_INHERITED_MEMB  = NO
++
++# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
++# path before files name in the file list and in the header files. If set 
++# to NO the shortest path that makes the file name unique will be used.
++
++FULL_PATH_NAMES        = YES
++
++# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
++# can be used to strip a user-defined part of the path. Stripping is 
++# only done if one of the specified strings matches the left-hand part of 
++# the path. The tag can be used to show relative paths in the file list. 
++# If left blank the directory from which doxygen is run is used as the 
++# path to strip.
++
++STRIP_FROM_PATH        = 
++
++# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
++# the path mentioned in the documentation of a class, which tells 
++# the reader which header file to include in order to use a class. 
++# If left blank only the name of the header file containing the class 
++# definition is used. Otherwise one should specify the include paths that 
++# are normally passed to the compiler using the -I flag.
++
++STRIP_FROM_INC_PATH    = 
++
++# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
++# (but less readable) file names. This can be useful is your file systems 
++# doesn't support long names like on DOS, Mac, or CD-ROM.
++
++SHORT_NAMES            = NO
++
++# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
++# will interpret the first line (until the first dot) of a JavaDoc-style 
++# comment as the brief description. If set to NO, the JavaDoc 
++# comments will behave just like regular Qt-style comments 
++# (thus requiring an explicit @brief command for a brief description.)
++
++JAVADOC_AUTOBRIEF      = NO
++
++# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
++# interpret the first line (until the first dot) of a Qt-style 
++# comment as the brief description. If set to NO, the comments 
++# will behave just like regular Qt-style comments (thus requiring 
++# an explicit \brief command for a brief description.)
++
++QT_AUTOBRIEF           = NO
++
++# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
++# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
++# comments) as a brief description. This used to be the default behaviour. 
++# The new default is to treat a multi-line C++ comment block as a detailed 
++# description. Set this tag to YES if you prefer the old behaviour instead.
++
++MULTILINE_CPP_IS_BRIEF = NO
++
++# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
++# member inherits the documentation from any documented member that it 
++# re-implements.
++
++INHERIT_DOCS           = YES
++
++# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
++# a new page for each member. If set to NO, the documentation of a member will 
++# be part of the file/class/namespace that contains it.
++
++SEPARATE_MEMBER_PAGES  = NO
++
++# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
++# Doxygen uses this value to replace tabs by spaces in code fragments.
++
++TAB_SIZE               = 8
++
++# This tag can be used to specify a number of aliases that acts 
++# as commands in the documentation. An alias has the form "name=value". 
++# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
++# put the command \sideeffect (or @sideeffect) in the documentation, which 
++# will result in a user-defined paragraph with heading "Side Effects:". 
++# You can put \n's in the value part of an alias to insert newlines.
++
++ALIASES                = 
++
++# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
++# sources only. Doxygen will then generate output that is more tailored for C. 
++# For instance, some of the names that are used will be different. The list 
++# of all members will be omitted, etc.
++
++OPTIMIZE_OUTPUT_FOR_C  = YES
++
++# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
++# sources only. Doxygen will then generate output that is more tailored for 
++# Java. For instance, namespaces will be presented as packages, qualified 
++# scopes will look different, etc.
++
++OPTIMIZE_OUTPUT_JAVA   = NO
++
++# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
++# sources only. Doxygen will then generate output that is more tailored for 
++# Fortran.
++
++OPTIMIZE_FOR_FORTRAN   = NO
++
++# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
++# sources. Doxygen will then generate output that is tailored for 
++# VHDL.
++
++OPTIMIZE_OUTPUT_VHDL   = NO
++
++# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
++# to include (a tag file for) the STL sources as input, then you should 
++# set this tag to YES in order to let doxygen match functions declarations and 
++# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
++# func(std::string) {}). This also make the inheritance and collaboration 
++# diagrams that involve STL classes more complete and accurate.
++
++BUILTIN_STL_SUPPORT    = NO
++
++# If you use Microsoft's C++/CLI language, you should set this option to YES to
++# enable parsing support.
++
++CPP_CLI_SUPPORT        = NO
++
++# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
++# Doxygen will parse them like normal C++ but will assume all classes use public 
++# instead of private inheritance when no explicit protection keyword is present.
++
++SIP_SUPPORT            = NO
++
++# For Microsoft's IDL there are propget and propput attributes to indicate getter 
++# and setter methods for a property. Setting this option to YES (the default) 
++# will make doxygen to replace the get and set methods by a property in the 
++# documentation. This will only work if the methods are indeed getting or 
++# setting a simple type. If this is not the case, or you want to show the 
++# methods anyway, you should set this option to NO.
++
++IDL_PROPERTY_SUPPORT   = YES
++
++# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
++# tag is set to YES, then doxygen will reuse the documentation of the first 
++# member in the group (if any) for the other members of the group. By default 
++# all members of a group must be documented explicitly.
++
++DISTRIBUTE_GROUP_DOC   = NO
++
++# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
++# the same type (for instance a group of public functions) to be put as a 
++# subgroup of that type (e.g. under the Public Functions section). Set it to 
++# NO to prevent subgrouping. Alternatively, this can be done per class using 
++# the \nosubgrouping command.
++
++SUBGROUPING            = YES
++
++# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
++# is documented as struct, union, or enum with the name of the typedef. So 
++# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
++# with name TypeT. When disabled the typedef will appear as a member of a file, 
++# namespace, or class. And the struct will be named TypeS. This can typically 
++# be useful for C code in case the coding convention dictates that all compound 
++# types are typedef'ed and only the typedef is referenced, never the tag name.
++
++TYPEDEF_HIDES_STRUCT   = NO
++
++# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
++# determine which symbols to keep in memory and which to flush to disk.
++# When the cache is full, less often used symbols will be written to disk.
++# For small to medium size projects (<1000 input files) the default value is 
++# probably good enough. For larger projects a too small cache size can cause 
++# doxygen to be busy swapping symbols to and from disk most of the time 
++# causing a significant performance penality. 
++# If the system has enough physical memory increasing the cache will improve the 
++# performance by keeping more symbols in memory. Note that the value works on 
++# a logarithmic scale so increasing the size by one will rougly double the 
++# memory usage. The cache size is given by this formula: 
++# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
++# corresponding to a cache size of 2^16 = 65536 symbols
++
++SYMBOL_CACHE_SIZE      = 0
++
++#---------------------------------------------------------------------------
++# Build related configuration options
++#---------------------------------------------------------------------------
++
++# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
++# documentation are documented, even if no documentation was available. 
++# Private class members and static file members will be hidden unless 
++# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
++
++EXTRACT_ALL            = YES
++
++# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
++# will be included in the documentation.
++
++EXTRACT_PRIVATE        = NO
++
++# If the EXTRACT_STATIC tag is set to YES all static members of a file 
++# will be included in the documentation.
++
++EXTRACT_STATIC         = NO
++
++# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
++# defined locally in source files will be included in the documentation. 
++# If set to NO only classes defined in header files are included.
++
++EXTRACT_LOCAL_CLASSES  = YES
++
++# This flag is only useful for Objective-C code. When set to YES local 
++# methods, which are defined in the implementation section but not in 
++# the interface are included in the documentation. 
++# If set to NO (the default) only methods in the interface are included.
++
++EXTRACT_LOCAL_METHODS  = NO
++
++# If this flag is set to YES, the members of anonymous namespaces will be 
++# extracted and appear in the documentation as a namespace called 
++# 'anonymous_namespace{file}', where file will be replaced with the base 
++# name of the file that contains the anonymous namespace. By default 
++# anonymous namespace are hidden.
++
++EXTRACT_ANON_NSPACES   = NO
++
++# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
++# undocumented members of documented classes, files or namespaces. 
++# If set to NO (the default) these members will be included in the 
++# various overviews, but no documentation section is generated. 
++# This option has no effect if EXTRACT_ALL is enabled.
++
++HIDE_UNDOC_MEMBERS     = NO
++
++# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
++# undocumented classes that are normally visible in the class hierarchy. 
++# If set to NO (the default) these classes will be included in the various 
++# overviews. This option has no effect if EXTRACT_ALL is enabled.
++
++HIDE_UNDOC_CLASSES     = NO
++
++# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
++# friend (class|struct|union) declarations. 
++# If set to NO (the default) these declarations will be included in the 
++# documentation.
++
++HIDE_FRIEND_COMPOUNDS  = NO
++
++# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
++# documentation blocks found inside the body of a function. 
++# If set to NO (the default) these blocks will be appended to the 
++# function's detailed documentation block.
++
++HIDE_IN_BODY_DOCS      = NO
++
++# The INTERNAL_DOCS tag determines if documentation 
++# that is typed after a \internal command is included. If the tag is set 
++# to NO (the default) then the documentation will be excluded. 
++# Set it to YES to include the internal documentation.
++
++INTERNAL_DOCS          = NO
++
++# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
++# file names in lower-case letters. If set to YES upper-case letters are also 
++# allowed. This is useful if you have classes or files whose names only differ 
++# in case and if your file system supports case sensitive file names. Windows 
++# and Mac users are advised to set this option to NO.
++
++CASE_SENSE_NAMES       = YES
++
++# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
++# will show members with their full class and namespace scopes in the 
++# documentation. If set to YES the scope will be hidden.
++
++HIDE_SCOPE_NAMES       = NO
++
++# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
++# will put a list of the files that are included by a file in the documentation 
++# of that file.
++
++SHOW_INCLUDE_FILES     = YES
++
++# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
++# is inserted in the documentation for inline members.
++
++INLINE_INFO            = YES
++
++# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
++# will sort the (detailed) documentation of file and class members 
++# alphabetically by member name. If set to NO the members will appear in 
++# declaration order.
++
++SORT_MEMBER_DOCS       = YES
++
++# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
++# brief documentation of file, namespace and class members alphabetically 
++# by member name. If set to NO (the default) the members will appear in 
++# declaration order.
++
++SORT_BRIEF_DOCS        = NO
++
++# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
++# hierarchy of group names into alphabetical order. If set to NO (the default) 
++# the group names will appear in their defined order.
++
++SORT_GROUP_NAMES       = NO
++
++# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
++# sorted by fully-qualified names, including namespaces. If set to 
++# NO (the default), the class list will be sorted only by class name, 
++# not including the namespace part. 
++# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
++# Note: This option applies only to the class list, not to the 
++# alphabetical list.
++
++SORT_BY_SCOPE_NAME     = NO
++
++# The GENERATE_TODOLIST tag can be used to enable (YES) or 
++# disable (NO) the todo list. This list is created by putting \todo 
++# commands in the documentation.
++
++GENERATE_TODOLIST      = YES
++
++# The GENERATE_TESTLIST tag can be used to enable (YES) or 
++# disable (NO) the test list. This list is created by putting \test 
++# commands in the documentation.
++
++GENERATE_TESTLIST      = YES
++
++# The GENERATE_BUGLIST tag can be used to enable (YES) or 
++# disable (NO) the bug list. This list is created by putting \bug 
++# commands in the documentation.
++
++GENERATE_BUGLIST       = YES
++
++# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
++# disable (NO) the deprecated list. This list is created by putting 
++# \deprecated commands in the documentation.
++
++GENERATE_DEPRECATEDLIST= YES
++
++# The ENABLED_SECTIONS tag can be used to enable conditional 
++# documentation sections, marked by \if sectionname ... \endif.
++
++ENABLED_SECTIONS       = 
++
++# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
++# the initial value of a variable or define consists of for it to appear in 
++# the documentation. If the initializer consists of more lines than specified 
++# here it will be hidden. Use a value of 0 to hide initializers completely. 
++# The appearance of the initializer of individual variables and defines in the 
++# documentation can be controlled using \showinitializer or \hideinitializer 
++# command in the documentation regardless of this setting.
++
++MAX_INITIALIZER_LINES  = 30
++
++# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
++# at the bottom of the documentation of classes and structs. If set to YES the 
++# list will mention the files that were used to generate the documentation.
++
++SHOW_USED_FILES        = YES
++
++# If the sources in your project are distributed over multiple directories 
++# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
++# in the documentation. The default is NO.
++
++SHOW_DIRECTORIES       = NO
++
++# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
++# This will remove the Files entry from the Quick Index and from the 
++# Folder Tree View (if specified). The default is YES.
++
++SHOW_FILES             = YES
++
++# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
++# Namespaces page.  This will remove the Namespaces entry from the Quick Index
++# and from the Folder Tree View (if specified). The default is YES.
++
++SHOW_NAMESPACES        = YES
++
++# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
++# doxygen should invoke to get the current version for each file (typically from 
++# the version control system). Doxygen will invoke the program by executing (via 
++# popen()) the command <command> <input-file>, where <command> is the value of 
++# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
++# provided by doxygen. Whatever the program writes to standard output 
++# is used as the file version. See the manual for examples.
++
++FILE_VERSION_FILTER    = 
++
++# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
++# doxygen. The layout file controls the global structure of the generated output files 
++# in an output format independent way. The create the layout file that represents 
++# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
++# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
++# of the layout file.
++
++LAYOUT_FILE            = 
++
++#---------------------------------------------------------------------------
++# configuration options related to warning and progress messages
++#---------------------------------------------------------------------------
++
++# The QUIET tag can be used to turn on/off the messages that are generated 
++# by doxygen. Possible values are YES and NO. If left blank NO is used.
++
++QUIET                  = YES
++
++# The WARNINGS tag can be used to turn on/off the warning messages that are 
++# generated by doxygen. Possible values are YES and NO. If left blank 
++# NO is used.
++
++WARNINGS               = YES
++
++# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
++# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
++# automatically be disabled.
++
++WARN_IF_UNDOCUMENTED   = YES
++
++# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
++# potential errors in the documentation, such as not documenting some 
++# parameters in a documented function, or documenting parameters that 
++# don't exist or using markup commands wrongly.
++
++WARN_IF_DOC_ERROR      = YES
++
++# This WARN_NO_PARAMDOC option can be abled to get warnings for 
++# functions that are documented, but have no documentation for their parameters 
++# or return value. If set to NO (the default) doxygen will only warn about 
++# wrong or incomplete parameter documentation, but not about the absence of 
++# documentation.
++
++WARN_NO_PARAMDOC       = NO
++
++# The WARN_FORMAT tag determines the format of the warning messages that 
++# doxygen can produce. The string should contain the $file, $line, and $text 
++# tags, which will be replaced by the file and line number from which the 
++# warning originated and the warning text. Optionally the format may contain 
++# $version, which will be replaced by the version of the file (if it could 
++# be obtained via FILE_VERSION_FILTER)
++
++WARN_FORMAT            = "$file:$line: $text"
++
++# The WARN_LOGFILE tag can be used to specify a file to which warning 
++# and error messages should be written. If left blank the output is written 
++# to stderr.
++
++WARN_LOGFILE           = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the input files
++#---------------------------------------------------------------------------
++
++# The INPUT tag can be used to specify the files and/or directories that contain 
++# documented source files. You may enter file names like "myfile.cpp" or 
++# directories like "/usr/src/myproject". Separate the files or directories 
++# with spaces.
++
++INPUT                  = 
++
++# This tag can be used to specify the character encoding of the source files 
++# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
++# also the default input encoding. Doxygen uses libiconv (or the iconv built 
++# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
++# the list of possible encodings.
++
++INPUT_ENCODING         = UTF-8
++
++# If the value of the INPUT tag contains directories, you can use the 
++# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
++# and *.h) to filter out the source-files in the directories. If left 
++# blank the following patterns are tested: 
++# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
++# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
++
++FILE_PATTERNS          = 
++
++# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
++# should be searched for input files as well. Possible values are YES and NO. 
++# If left blank NO is used.
++
++RECURSIVE              = NO
++
++# The EXCLUDE tag can be used to specify files and/or directories that should 
++# excluded from the INPUT source files. This way you can easily exclude a 
++# subdirectory from a directory tree whose root is specified with the INPUT tag.
++
++EXCLUDE                = 
++
++# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
++# directories that are symbolic links (a Unix filesystem feature) are excluded 
++# from the input.
++
++EXCLUDE_SYMLINKS       = NO
++
++# If the value of the INPUT tag contains directories, you can use the 
++# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
++# certain files from those directories. Note that the wildcards are matched 
++# against the file with absolute path, so to exclude all test directories 
++# for example use the pattern */test/*
++
++EXCLUDE_PATTERNS       = 
++
++# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
++# (namespaces, classes, functions, etc.) that should be excluded from the 
++# output. The symbol name can be a fully qualified name, a word, or if the 
++# wildcard * is used, a substring. Examples: ANamespace, AClass, 
++# AClass::ANamespace, ANamespace::*Test
++
++EXCLUDE_SYMBOLS        = 
++
++# The EXAMPLE_PATH tag can be used to specify one or more files or 
++# directories that contain example code fragments that are included (see 
++# the \include command).
++
++EXAMPLE_PATH           = 
++
++# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
++# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
++# and *.h) to filter out the source-files in the directories. If left 
++# blank all files are included.
++
++EXAMPLE_PATTERNS       = 
++
++# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
++# searched for input files to be used with the \include or \dontinclude 
++# commands irrespective of the value of the RECURSIVE tag. 
++# Possible values are YES and NO. If left blank NO is used.
++
++EXAMPLE_RECURSIVE      = NO
++
++# The IMAGE_PATH tag can be used to specify one or more files or 
++# directories that contain image that are included in the documentation (see 
++# the \image command).
++
++IMAGE_PATH             = 
++
++# The INPUT_FILTER tag can be used to specify a program that doxygen should 
++# invoke to filter for each input file. Doxygen will invoke the filter program 
++# by executing (via popen()) the command <filter> <input-file>, where <filter> 
++# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
++# input file. Doxygen will then use the output that the filter program writes 
++# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
++# ignored.
++
++INPUT_FILTER           = 
++
++# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
++# basis.  Doxygen will compare the file name with each pattern and apply the 
++# filter if there is a match.  The filters are a list of the form: 
++# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
++# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
++# is applied to all files.
++
++FILTER_PATTERNS        = 
++
++# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
++# INPUT_FILTER) will be used to filter the input files when producing source 
++# files to browse (i.e. when SOURCE_BROWSER is set to YES).
++
++FILTER_SOURCE_FILES    = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to source browsing
++#---------------------------------------------------------------------------
++
++# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
++# be generated. Documented entities will be cross-referenced with these sources. 
++# Note: To get rid of all source code in the generated output, make sure also 
++# VERBATIM_HEADERS is set to NO.
++
++SOURCE_BROWSER         = NO
++
++# Setting the INLINE_SOURCES tag to YES will include the body 
++# of functions and classes directly in the documentation.
++
++INLINE_SOURCES         = NO
++
++# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
++# doxygen to hide any special comment blocks from generated source code 
++# fragments. Normal C and C++ comments will always remain visible.
++
++STRIP_CODE_COMMENTS    = YES
++
++# If the REFERENCED_BY_RELATION tag is set to YES 
++# then for each documented function all documented 
++# functions referencing it will be listed.
++
++REFERENCED_BY_RELATION = NO
++
++# If the REFERENCES_RELATION tag is set to YES 
++# then for each documented function all documented entities 
++# called/used by that function will be listed.
++
++REFERENCES_RELATION    = NO
++
++# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
++# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
++# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
++# link to the source code.  Otherwise they will link to the documentstion.
++
++REFERENCES_LINK_SOURCE = YES
++
++# If the USE_HTAGS tag is set to YES then the references to source code 
++# will point to the HTML generated by the htags(1) tool instead of doxygen 
++# built-in source browser. The htags tool is part of GNU's global source 
++# tagging system (see http://www.gnu.org/software/global/global.html). You 
++# will need version 4.8.6 or higher.
++
++USE_HTAGS              = NO
++
++# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
++# will generate a verbatim copy of the header file for each class for 
++# which an include is specified. Set to NO to disable this.
++
++VERBATIM_HEADERS       = YES
++
++#---------------------------------------------------------------------------
++# configuration options related to the alphabetical class index
++#---------------------------------------------------------------------------
++
++# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
++# of all compounds will be generated. Enable this if the project 
++# contains a lot of classes, structs, unions or interfaces.
++
++ALPHABETICAL_INDEX     = NO
++
++# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
++# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
++# in which this list will be split (can be a number in the range [1..20])
++
++COLS_IN_ALPHA_INDEX    = 5
++
++# In case all classes in a project start with a common prefix, all 
++# classes will be put under the same header in the alphabetical index. 
++# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
++# should be ignored while generating the index headers.
++
++IGNORE_PREFIX          = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the HTML output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
++# generate HTML output.
++
++GENERATE_HTML          = YES
++
++# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `html' will be used as the default path.
++
++HTML_OUTPUT            = html
++
++# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
++# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
++# doxygen will generate files with .html extension.
++
++HTML_FILE_EXTENSION    = .html
++
++# The HTML_HEADER tag can be used to specify a personal HTML header for 
++# each generated HTML page. If it is left blank doxygen will generate a 
++# standard header.
++
++HTML_HEADER            = 
++
++# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
++# each generated HTML page. If it is left blank doxygen will generate a 
++# standard footer.
++
++HTML_FOOTER            = no_date_footer.html
++
++# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
++# style sheet that is used by each HTML page. It can be used to 
++# fine-tune the look of the HTML output. If the tag is left blank doxygen 
++# will generate a default style sheet. Note that doxygen will try to copy 
++# the style sheet file to the HTML output directory, so don't put your own 
++# stylesheet in the HTML output directory as well, or it will be erased!
++
++HTML_STYLESHEET        = 
++
++# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
++# files or namespaces will be aligned in HTML using tables. If set to 
++# NO a bullet list will be used.
++
++HTML_ALIGN_MEMBERS     = YES
++
++# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
++# documentation will contain sections that can be hidden and shown after the 
++# page has loaded. For this to work a browser that supports 
++# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
++# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
++
++HTML_DYNAMIC_SECTIONS  = NO
++
++# If the GENERATE_DOCSET tag is set to YES, additional index files 
++# will be generated that can be used as input for Apple's Xcode 3 
++# integrated development environment, introduced with OSX 10.5 (Leopard). 
++# To create a documentation set, doxygen will generate a Makefile in the 
++# HTML output directory. Running make will produce the docset in that 
++# directory and running "make install" will install the docset in 
++# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
++# it at startup. 
++# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
++
++GENERATE_DOCSET        = NO
++
++# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
++# feed. A documentation feed provides an umbrella under which multiple 
++# documentation sets from a single provider (such as a company or product suite) 
++# can be grouped.
++
++DOCSET_FEEDNAME        = "Doxygen generated docs"
++
++# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
++# should uniquely identify the documentation set bundle. This should be a 
++# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
++# will append .docset to the name.
++
++DOCSET_BUNDLE_ID       = org.doxygen.Project
++
++# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
++# will be generated that can be used as input for tools like the 
++# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
++# of the generated HTML documentation.
++
++GENERATE_HTMLHELP      = NO
++
++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
++# be used to specify the file name of the resulting .chm file. You 
++# can add a path in front of the file if the result should not be 
++# written to the html output directory.
++
++CHM_FILE               = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
++# be used to specify the location (absolute path including file name) of 
++# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
++# the HTML help compiler on the generated index.hhp.
++
++HHC_LOCATION           = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
++# controls if a separate .chi index file is generated (YES) or that 
++# it should be included in the master .chm file (NO).
++
++GENERATE_CHI           = NO
++
++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
++# is used to encode HtmlHelp index (hhk), content (hhc) and project file
++# content.
++
++CHM_INDEX_ENCODING     = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
++# controls whether a binary table of contents is generated (YES) or a 
++# normal table of contents (NO) in the .chm file.
++
++BINARY_TOC             = NO
++
++# The TOC_EXPAND flag can be set to YES to add extra items for group members 
++# to the contents of the HTML help documentation and to the tree view.
++
++TOC_EXPAND             = NO
++
++# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
++# are set, an additional index file will be generated that can be used as input for 
++# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
++# HTML documentation.
++
++GENERATE_QHP           = NO
++
++# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
++# be used to specify the file name of the resulting .qch file. 
++# The path specified is relative to the HTML output folder.
++
++QCH_FILE               = 
++
++# The QHP_NAMESPACE tag specifies the namespace to use when generating 
++# Qt Help Project output. For more information please see 
++# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
++
++QHP_NAMESPACE          = org.doxygen.Project
++
++# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
++# Qt Help Project output. For more information please see 
++# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
++
++QHP_VIRTUAL_FOLDER     = doc
++
++# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
++# be used to specify the location of Qt's qhelpgenerator. 
++# If non-empty doxygen will try to run qhelpgenerator on the generated 
++# .qhp file .
++
++QHG_LOCATION           = 
++
++# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
++# top of each HTML page. The value NO (the default) enables the index and 
++# the value YES disables it.
++
++DISABLE_INDEX          = NO
++
++# This tag can be used to set the number of enum values (range [1..20]) 
++# that doxygen will group on one line in the generated HTML documentation.
++
++ENUM_VALUES_PER_LINE   = 4
++
++# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
++# structure should be generated to display hierarchical information.
++# If the tag value is set to FRAME, a side panel will be generated
++# containing a tree-like index structure (just like the one that 
++# is generated for HTML Help). For this to work a browser that supports 
++# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
++# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
++# probably better off using the HTML help feature. Other possible values 
++# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
++# and Class Hierarchy pages using a tree view instead of an ordered list;
++# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
++# disables this behavior completely. For backwards compatibility with previous
++# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
++# respectively.
++
++GENERATE_TREEVIEW      = NONE
++
++# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
++# used to set the initial width (in pixels) of the frame in which the tree 
++# is shown.
++
++TREEVIEW_WIDTH         = 250
++
++# Use this tag to change the font size of Latex formulas included 
++# as images in the HTML documentation. The default is 10. Note that 
++# when you change the font size after a successful doxygen run you need 
++# to manually remove any form_*.png images from the HTML output directory 
++# to force them to be regenerated.
++
++FORMULA_FONTSIZE       = 10
++
++#---------------------------------------------------------------------------
++# configuration options related to the LaTeX output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
++# generate Latex output.
++
++GENERATE_LATEX         = NO
++
++# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `latex' will be used as the default path.
++
++LATEX_OUTPUT           = latex
++
++# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
++# invoked. If left blank `latex' will be used as the default command name.
++
++LATEX_CMD_NAME         = latex
++
++# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
++# generate index for LaTeX. If left blank `makeindex' will be used as the 
++# default command name.
++
++MAKEINDEX_CMD_NAME     = makeindex
++
++# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
++# LaTeX documents. This may be useful for small projects and may help to 
++# save some trees in general.
++
++COMPACT_LATEX          = NO
++
++# The PAPER_TYPE tag can be used to set the paper type that is used 
++# by the printer. Possible values are: a4, a4wide, letter, legal and 
++# executive. If left blank a4wide will be used.
++
++PAPER_TYPE             = a4wide
++
++# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
++# packages that should be included in the LaTeX output.
++
++EXTRA_PACKAGES         = 
++
++# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
++# the generated latex document. The header should contain everything until 
++# the first chapter. If it is left blank doxygen will generate a 
++# standard header. Notice: only use this tag if you know what you are doing!
++
++LATEX_HEADER           = 
++
++# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
++# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
++# contain links (just like the HTML output) instead of page references 
++# This makes the output suitable for online browsing using a pdf viewer.
++
++PDF_HYPERLINKS         = YES
++
++# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
++# plain latex in the generated Makefile. Set this option to YES to get a 
++# higher quality PDF documentation.
++
++USE_PDFLATEX           = YES
++
++# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
++# command to the generated LaTeX files. This will instruct LaTeX to keep 
++# running if errors occur, instead of asking the user for help. 
++# This option is also used when generating formulas in HTML.
++
++LATEX_BATCHMODE        = NO
++
++# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
++# include the index chapters (such as File Index, Compound Index, etc.) 
++# in the output.
++
++LATEX_HIDE_INDICES     = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the RTF output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
++# The RTF output is optimized for Word 97 and may not look very pretty with 
++# other RTF readers or editors.
++
++GENERATE_RTF           = NO
++
++# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `rtf' will be used as the default path.
++
++RTF_OUTPUT             = rtf
++
++# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
++# RTF documents. This may be useful for small projects and may help to 
++# save some trees in general.
++
++COMPACT_RTF            = NO
++
++# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
++# will contain hyperlink fields. The RTF file will 
++# contain links (just like the HTML output) instead of page references. 
++# This makes the output suitable for online browsing using WORD or other 
++# programs which support those fields. 
++# Note: wordpad (write) and others do not support links.
++
++RTF_HYPERLINKS         = NO
++
++# Load stylesheet definitions from file. Syntax is similar to doxygen's 
++# config file, i.e. a series of assignments. You only have to provide 
++# replacements, missing definitions are set to their default value.
++
++RTF_STYLESHEET_FILE    = 
++
++# Set optional variables used in the generation of an rtf document. 
++# Syntax is similar to doxygen's config file.
++
++RTF_EXTENSIONS_FILE    = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the man page output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
++# generate man pages
++
++GENERATE_MAN           = NO
++
++# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `man' will be used as the default path.
++
++MAN_OUTPUT             = man
++
++# The MAN_EXTENSION tag determines the extension that is added to 
++# the generated man pages (default is the subroutine's section .3)
++
++MAN_EXTENSION          = .3
++
++# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
++# then it will generate one additional man file for each entity 
++# documented in the real man page(s). These additional files 
++# only source the real man page, but without them the man command 
++# would be unable to find the correct page. The default is NO.
++
++MAN_LINKS              = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the XML output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_XML tag is set to YES Doxygen will 
++# generate an XML file that captures the structure of 
++# the code including all documentation.
++
++GENERATE_XML           = NO
++
++# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `xml' will be used as the default path.
++
++XML_OUTPUT             = xml
++
++# The XML_SCHEMA tag can be used to specify an XML schema, 
++# which can be used by a validating XML parser to check the 
++# syntax of the XML files.
++
++XML_SCHEMA             = 
++
++# The XML_DTD tag can be used to specify an XML DTD, 
++# which can be used by a validating XML parser to check the 
++# syntax of the XML files.
++
++XML_DTD                = 
++
++# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
++# dump the program listings (including syntax highlighting 
++# and cross-referencing information) to the XML output. Note that 
++# enabling this will significantly increase the size of the XML output.
++
++XML_PROGRAMLISTING     = YES
++
++#---------------------------------------------------------------------------
++# configuration options for the AutoGen Definitions output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
++# generate an AutoGen Definitions (see autogen.sf.net) file 
++# that captures the structure of the code including all 
++# documentation. Note that this feature is still experimental 
++# and incomplete at the moment.
++
++GENERATE_AUTOGEN_DEF   = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the Perl module output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
++# generate a Perl module file that captures the structure of 
++# the code including all documentation. Note that this 
++# feature is still experimental and incomplete at the 
++# moment.
++
++GENERATE_PERLMOD       = NO
++
++# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
++# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
++# to generate PDF and DVI output from the Perl module output.
++
++PERLMOD_LATEX          = NO
++
++# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
++# nicely formatted so it can be parsed by a human reader.  This is useful 
++# if you want to understand what is going on.  On the other hand, if this 
++# tag is set to NO the size of the Perl module output will be much smaller 
++# and Perl will parse it just the same.
++
++PERLMOD_PRETTY         = YES
++
++# The names of the make variables in the generated doxyrules.make file 
++# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
++# This is useful so different doxyrules.make files included by the same 
++# Makefile don't overwrite each other's variables.
++
++PERLMOD_MAKEVAR_PREFIX = 
++
++#---------------------------------------------------------------------------
++# Configuration options related to the preprocessor   
++#---------------------------------------------------------------------------
++
++# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
++# evaluate all C-preprocessor directives found in the sources and include 
++# files.
++
++ENABLE_PREPROCESSING   = YES
++
++# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
++# names in the source code. If set to NO (the default) only conditional 
++# compilation will be performed. Macro expansion can be done in a controlled 
++# way by setting EXPAND_ONLY_PREDEF to YES.
++
++MACRO_EXPANSION        = NO
++
++# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
++# then the macro expansion is limited to the macros specified with the 
++# PREDEFINED and EXPAND_AS_DEFINED tags.
++
++EXPAND_ONLY_PREDEF     = NO
++
++# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
++# in the INCLUDE_PATH (see below) will be search if a #include is found.
++
++SEARCH_INCLUDES        = YES
++
++# The INCLUDE_PATH tag can be used to specify one or more directories that 
++# contain include files that are not input files but should be processed by 
++# the preprocessor.
++
++INCLUDE_PATH           = 
++
++# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
++# patterns (like *.h and *.hpp) to filter out the header-files in the 
++# directories. If left blank, the patterns specified with FILE_PATTERNS will 
++# be used.
++
++INCLUDE_FILE_PATTERNS  = 
++
++# The PREDEFINED tag can be used to specify one or more macro names that 
++# are defined before the preprocessor is started (similar to the -D option of 
++# gcc). The argument of the tag is a list of macros of the form: name 
++# or name=definition (no spaces). If the definition and the = are 
++# omitted =1 is assumed. To prevent a macro definition from being 
++# undefined via #undef or recursively expanded use the := operator 
++# instead of the = operator.
++
++PREDEFINED             = 
++
++# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
++# this tag can be used to specify a list of macro names that should be expanded. 
++# The macro definition that is found in the sources will be used. 
++# Use the PREDEFINED tag if you want to use a different macro definition.
++
++EXPAND_AS_DEFINED      = 
++
++# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
++# doxygen's preprocessor will remove all function-like macros that are alone 
++# on a line, have an all uppercase name, and do not end with a semicolon. Such 
++# function macros are typically used for boiler-plate code, and will confuse 
++# the parser if not removed.
++
++SKIP_FUNCTION_MACROS   = YES
++
++#---------------------------------------------------------------------------
++# Configuration::additions related to external references   
++#---------------------------------------------------------------------------
++
++# The TAGFILES option can be used to specify one or more tagfiles. 
++# Optionally an initial location of the external documentation 
++# can be added for each tagfile. The format of a tag file without 
++# this location is as follows: 
++#   TAGFILES = file1 file2 ... 
++# Adding location for the tag files is done as follows: 
++#   TAGFILES = file1=loc1 "file2 = loc2" ... 
++# where "loc1" and "loc2" can be relative or absolute paths or 
++# URLs. If a location is present for each tag, the installdox tool 
++# does not have to be run to correct the links.
++# Note that each tag file must have a unique name
++# (where the name does NOT include the path)
++# If a tag file is not located in the directory in which doxygen 
++# is run, you must also specify the path to the tagfile here.
++
++TAGFILES               = 
++
++# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
++# a tag file that is based on the input files it reads.
++
++GENERATE_TAGFILE       = 
++
++# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
++# in the class index. If set to NO only the inherited external classes 
++# will be listed.
++
++ALLEXTERNALS           = NO
++
++# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
++# in the modules index. If set to NO, only the current project's groups will 
++# be listed.
++
++EXTERNAL_GROUPS        = YES
++
++# The PERL_PATH should be the absolute path and name of the perl script 
++# interpreter (i.e. the result of `which perl').
++
++PERL_PATH              = /usr/bin/perl
++
++#---------------------------------------------------------------------------
++# Configuration options related to the dot tool   
++#---------------------------------------------------------------------------
++
++# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
++# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
++# or super classes. Setting the tag to NO turns the diagrams off. Note that 
++# this option is superseded by the HAVE_DOT option below. This is only a 
++# fallback. It is recommended to install and use dot, since it yields more 
++# powerful graphs.
++
++CLASS_DIAGRAMS         = YES
++
++# You can define message sequence charts within doxygen comments using the \msc 
++# command. Doxygen will then run the mscgen tool (see 
++# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
++# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
++# the mscgen tool resides. If left empty the tool is assumed to be found in the 
++# default search path.
++
++MSCGEN_PATH            = 
++
++# If set to YES, the inheritance and collaboration graphs will hide 
++# inheritance and usage relations if the target is undocumented 
++# or is not a class.
++
++HIDE_UNDOC_RELATIONS   = YES
++
++# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
++# available from the path. This tool is part of Graphviz, a graph visualization 
++# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
++# have no effect if this option is set to NO (the default)
++
++HAVE_DOT               = NO
++
++# By default doxygen will write a font called FreeSans.ttf to the output 
++# directory and reference it in all dot files that doxygen generates. This 
++# font does not include all possible unicode characters however, so when you need 
++# these (or just want a differently looking font) you can specify the font name 
++# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
++# which can be done by putting it in a standard location or by setting the 
++# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
++# containing the font.
++
++DOT_FONTNAME           = FreeSans
++
++# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
++# The default size is 10pt.
++
++DOT_FONTSIZE           = 10
++
++# By default doxygen will tell dot to use the output directory to look for the 
++# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
++# different font using DOT_FONTNAME you can set the path where dot 
++# can find it using this tag.
++
++DOT_FONTPATH           = 
++
++# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for each documented class showing the direct and 
++# indirect inheritance relations. Setting this tag to YES will force the 
++# the CLASS_DIAGRAMS tag to NO.
++
++CLASS_GRAPH            = YES
++
++# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for each documented class showing the direct and 
++# indirect implementation dependencies (inheritance, containment, and 
++# class references variables) of the class with other documented classes.
++
++COLLABORATION_GRAPH    = YES
++
++# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for groups, showing the direct groups dependencies
++
++GROUP_GRAPHS           = YES
++
++# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
++# collaboration diagrams in a style similar to the OMG's Unified Modeling 
++# Language.
++
++UML_LOOK               = NO
++
++# If set to YES, the inheritance and collaboration graphs will show the 
++# relations between templates and their instances.
++
++TEMPLATE_RELATIONS     = NO
++
++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
++# tags are set to YES then doxygen will generate a graph for each documented 
++# file showing the direct and indirect include dependencies of the file with 
++# other documented files.
++
++INCLUDE_GRAPH          = YES
++
++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
++# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
++# documented header file showing the documented files that directly or 
++# indirectly include this file.
++
++INCLUDED_BY_GRAPH      = YES
++
++# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
++# doxygen will generate a call dependency graph for every global function 
++# or class method. Note that enabling this option will significantly increase 
++# the time of a run. So in most cases it will be better to enable call graphs 
++# for selected functions only using the \callgraph command.
++
++CALL_GRAPH             = NO
++
++# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
++# doxygen will generate a caller dependency graph for every global function 
++# or class method. Note that enabling this option will significantly increase 
++# the time of a run. So in most cases it will be better to enable caller 
++# graphs for selected functions only using the \callergraph command.
++
++CALLER_GRAPH           = NO
++
++# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
++# will graphical hierarchy of all classes instead of a textual one.
++
++GRAPHICAL_HIERARCHY    = YES
++
++# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
++# then doxygen will show the dependencies a directory has on other directories 
++# in a graphical way. The dependency relations are determined by the #include
++# relations between the files in the directories.
++
++DIRECTORY_GRAPH        = YES
++
++# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
++# generated by dot. Possible values are png, jpg, or gif
++# If left blank png will be used.
++
++DOT_IMAGE_FORMAT       = png
++
++# The tag DOT_PATH can be used to specify the path where the dot tool can be 
++# found. If left blank, it is assumed the dot tool can be found in the path.
++
++DOT_PATH               = 
++
++# The DOTFILE_DIRS tag can be used to specify one or more directories that 
++# contain dot files that are included in the documentation (see the 
++# \dotfile command).
++
++DOTFILE_DIRS           = 
++
++# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
++# nodes that will be shown in the graph. If the number of nodes in a graph 
++# becomes larger than this value, doxygen will truncate the graph, which is 
++# visualized by representing a node as a red box. Note that doxygen if the 
++# number of direct children of the root node in a graph is already larger than 
++# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
++# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
++
++DOT_GRAPH_MAX_NODES    = 50
++
++# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
++# graphs generated by dot. A depth value of 3 means that only nodes reachable 
++# from the root by following a path via at most 3 edges will be shown. Nodes 
++# that lay further from the root node will be omitted. Note that setting this 
++# option to 1 or 2 may greatly reduce the computation time needed for large 
++# code bases. Also note that the size of a graph can be further restricted by 
++# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
++
++MAX_DOT_GRAPH_DEPTH    = 0
++
++# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
++# background. This is disabled by default, because dot on Windows does not 
++# seem to support this out of the box. Warning: Depending on the platform used, 
++# enabling this option may lead to badly anti-aliased labels on the edges of 
++# a graph (i.e. they become hard to read).
++
++DOT_TRANSPARENT        = NO
++
++# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
++# files in one run (i.e. multiple -o and -T options on the command line). This 
++# makes dot run faster, but since only newer versions of dot (>1.8.10) 
++# support this, this feature is disabled by default.
++
++DOT_MULTI_TARGETS      = NO
++
++# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
++# generate a legend page explaining the meaning of the various boxes and 
++# arrows in the dot generated graphs.
++
++GENERATE_LEGEND        = YES
++
++# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
++# remove the intermediate dot files that are used to generate 
++# the various graphs.
++
++DOT_CLEANUP            = YES
++
++#---------------------------------------------------------------------------
++# Configuration::additions related to the search engine   
++#---------------------------------------------------------------------------
++
++# The SEARCHENGINE tag specifies whether or not a search engine should be 
++# used. If set to NO the values of all tags below this one will be ignored.
++
++SEARCHENGINE           = NO
+diff --git a/libiscsi/libiscsi.h b/libiscsi/libiscsi.h
+new file mode 100644
+index 0000000..756590e
+--- /dev/null
++++ b/libiscsi/libiscsi.h
+@@ -0,0 +1,344 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede at redhat.com>
++ * maintained by open-iscsi at googlegroups.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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#ifndef __LIBISCSI_H
++#define __LIBISCSI_H
++
++#include <netdb.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++#if __GNUC__ >= 4
++#define PUBLIC __attribute__ ((visibility("default")))
++#else
++#define PUBLIC
++#endif
++
++/** \brief Maximum length for iSCSI values.
++ *
++ * Maximum length for iSCSI values such as hostnames and parameter values.
++ */
++#define LIBISCSI_VALUE_MAXLEN 256
++
++/** \brief supported authentication methods
++ *
++ * This enum lists all supported authentication methods.
++ */
++enum libiscsi_auth_t {
++    libiscsi_auth_none   /** No authentication */,
++    libiscsi_auth_chap   /** CHAP authentication */,
++};
++
++/** \brief libiscsi context struct
++ *
++ * Note: even though libiscsi uses a context struct, the underlying open-iscsi
++ * code does not, so libiscsi is not thread safe, not even when using one
++ * context per thread!
++ */
++struct libiscsi_context;
++
++/** \brief iSCSI node record
++ *
++ * Struct holding data uniquely identifying an iSCSI node.
++ */
++struct libiscsi_node {
++    char name[LIBISCSI_VALUE_MAXLEN]     /** iSCSI iqn for the node. */;
++    int tpgt                             /** Portal group number. */;
++    /* Note open-iscsi has some code in place for multiple connections in one
++       node record and thus multiple address / port combi's, but this does not
++       get used anywhere, so we keep things simple and assume one connection */
++    char address[NI_MAXHOST]             /** Portal hostname or IP-address. */;
++    int port                             /** Portal port number. */;
++    char iface[LIBISCSI_VALUE_MAXLEN]    /** Interface to connect through. */;
++};
++
++/** \brief libiscsi CHAP authentication information struct
++ *
++ * Struct holding all data needed for CHAP login / authentication. Note that
++ * \e reverse_username may be a 0 length string in which case only forward
++ * authentication will be done.
++ */
++struct libiscsi_chap_auth_info {
++    char username[LIBISCSI_VALUE_MAXLEN]         /** Username */;
++    char password[LIBISCSI_VALUE_MAXLEN]         /** Password */;
++    char reverse_username[LIBISCSI_VALUE_MAXLEN] /** Reverse Username */;
++    char reverse_password[LIBISCSI_VALUE_MAXLEN] /** Reverse Password */;
++};
++
++/** \brief generic libiscsi authentication information struct
++ *
++ * Struct holding authentication information for discovery and login.
++ */
++struct libiscsi_auth_info {
++    enum libiscsi_auth_t method /** Authentication method to use */;
++    union {
++        struct libiscsi_chap_auth_info chap /** Chap specific info */;
++    } /** Union holding method depenend info */;
++};
++
++/** \brief Initalize libiscsi
++ *
++ * This function creates a libiscsi context and initalizes it. This context
++ * is need to use other libiscsi funtions.
++ *
++ * \return     A pointer to the created context, or NULL in case of an error.
++ */
++PUBLIC struct libiscsi_context *libiscsi_init(void);
++
++/** \brief Cleanup libiscsi used resource
++ *
++ * This function cleanups any used resources and then destroys the passed
++ * context. After this the passed in context may no longer be used!
++ *
++ * \param context                libiscsi context to operate on.
++ */
++PUBLIC void libiscsi_cleanup(struct libiscsi_context *context);
++
++/** \brief Discover iSCSI nodes using sendtargets and add them to the node db.
++ *
++ * This function connects to the given address and port and then tries to
++ * discover iSCSI nodes using the sendtargets protocol. Any found nodes are
++ * added to the local iSCSI node database and are returned in a dynamically
++ * allocated array.
++ *
++ * Note that the (optional) authentication info is for authenticating the
++ * discovery, and is not for the found nodes! If the connection(s) to the
++ * node(s) need authentication too, you can set the username / password for
++ * those (which can be different!) using the libiscsi_node_set_auth() function.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param address                Hostname or IP-address to connect to.
++ * \param port                   Port to connect to, or 0 for the default port.
++ * \param auth_info              Authentication information, or NULL.
++ * \param nr_found		 The number of found nodes will be returned
++ *                               through this pointer if not NULL.
++ * \param found_nodes            The address of the dynamically allocated array
++ *                               of found nodes will be returned through this
++ *                               pointer if not NULL. The caller must free this
++ *                               array using free().
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_discover_sendtargets(struct libiscsi_context *context,
++    const char *address, int port, const struct libiscsi_auth_info *auth_info,
++    int *nr_found, struct libiscsi_node **found_nodes);
++
++/** \brief Read iSCSI node info from firmware and add them to the node db.
++ *
++ * This function discovers iSCSI nodes using firmware (ppc or ibft). Any found
++ * nodes are added to the local iSCSI node database and are returned in a
++ * dynamically allocated array.
++ *
++ * Note that unlike sendtargets discovery, this function will also read
++ * authentication info and store that in the database too.
++ *
++ * Note this function currently is a stub which will always return -EINVAL
++ * (IOW it is not yet implemented)
++ *
++ * \param context                libiscsi context to operate on.
++ * \param nr_found		 The number of found nodes will be returned
++ *                               through this pointer if not NULL.
++ * \param found_nodes            The address of the dynamically allocated array
++ *                               of found nodes will be returned through this
++ *                               pointer if not NULL. The caller must free this
++ *                               array using free().
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_discover_firmware(struct libiscsi_context *context,
++    int *nr_found, struct libiscsi_node **found_nodes);
++
++/** \brief Check validity of the given authentication info.
++ *
++ * This function checks the validity of the given authentication info. For 
++ * example in case of CHAP, if the username and password are not empty.
++ *
++ * This function is mainly intended for use by language bindings.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param auth_info              Authentication information to check.
++ * \return                       0 on success, otherwise EINVAL.
++ */
++PUBLIC int libiscsi_verify_auth_info(struct libiscsi_context *context,
++	const struct libiscsi_auth_info *auth_info);
++
++/** \brief Set the authentication info for the given node.
++ *
++ * This function sets the authentication information for the node described by
++ * the given node record. This will overwrite any existing authentication
++ * information.
++ *
++ * This is the way to specify authentication information for nodes found
++ * through sendtargets discovery.
++ *
++ * Note:
++ * 1) This is a convience wrapper around libiscsi_node_set_parameter(),
++ *    setting the node.session.auth.* parameters.
++ * 2) For nodes found through firmware discovery the authentication information
++ *    has already been set from the firmware.
++ * 3) \e auth_info may be NULL in which case any existing authinfo will be
++ *    cleared.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param node                   iSCSI node to set auth information of
++ * \param auth_info              Authentication information, or NULL.
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_node_set_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const struct libiscsi_auth_info *auth_info);
++
++/** \brief Get the authentication info for the given node.
++ *
++ * This function gets the authentication information for the node described by
++ * the given node record.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param node                   iSCSI node to set auth information of
++ * \param auth_info              Pointer to a libiscsi_auth_info struct where
++ *                               the retreived information will be stored.
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_node_get_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    struct libiscsi_auth_info *auth_info);
++
++/** \brief Login to an iSCSI node.
++ *
++ * Login to the iSCSI node described by the given node record.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to login to.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_login(struct libiscsi_context *context,
++    const struct libiscsi_node *node);
++
++/** \brief Logout of an iSCSI node.
++ *
++ * Logout of the iSCSI node described by the given node record.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to logout from.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_logout(struct libiscsi_context *context,
++    const struct libiscsi_node *node);
++
++/** \brief Set an iSCSI parameter for the given node
++ *
++ * Set the given nodes iSCSI parameter named by \e parameter to value \e value.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to change a parameter from.
++ * \param parameter     Name of the parameter to set.
++ * \param value         Value to set the parameter too.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_set_parameter(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const char *parameter, const char *value);
++
++/** \brief Get the value of an iSCSI parameter for the given node
++ *
++ * Get the value of the given nodes iSCSI parameter named by \e parameter.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to change a parameter from.
++ * \param parameter     Name of the parameter to get.
++ * \param value         The retreived value is stored here, this buffer must be
++ *                      atleast LIBISCSI_VALUE_MAXLEN bytes large.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_get_parameter(struct libiscsi_context *context,
++    const struct libiscsi_node *node, const char *parameter, char *value);
++
++/** \brief Get human readable string describing the last libiscsi error.
++ *
++ * This function can be called to get a human readable error string when a
++ * libiscsi function has returned an error. This function uses a single buffer
++ * per context, thus the result is only valid as long as no other libiscsi
++ * calls are made on the same context after the failing function call.
++ *
++ * \param context       libiscsi context to operate on.
++ *
++ * \return human readable string describing the last libiscsi error.
++ */
++PUBLIC const char *libiscsi_get_error_string(struct libiscsi_context *context);
++
++
++/************************** Utility functions *******************************/
++
++/** \brief libiscsi network config struct
++ *
++ * libiscsi network config struct.
++ */
++struct libiscsi_network_config {
++    int dhcp                                  /** Using DHCP? (boolean). */;
++    char iface_name[LIBISCSI_VALUE_MAXLEN]    /** Interface name. */;
++    char mac_address[LIBISCSI_VALUE_MAXLEN]   /** MAC address. */;
++    char ip_address[LIBISCSI_VALUE_MAXLEN]    /** IP address. */;
++    char netmask[LIBISCSI_VALUE_MAXLEN]       /** Netmask. */;
++    char gateway[LIBISCSI_VALUE_MAXLEN]       /** IP of Default gateway. */;
++    char primary_dns[LIBISCSI_VALUE_MAXLEN]   /** IP of the Primary DNS. */;
++    char secondary_dns[LIBISCSI_VALUE_MAXLEN] /** IP of the Secondary DNS. */;
++};
++
++/** \brief Get network configuration information from iscsi firmware
++ *
++ * Function can be called to get the network configuration information
++ * (like dhcp, ip, netmask, default gateway, etc.) from the firmware of a
++ * network adapter with iscsi boot firmware.
++ *
++ * Note that not all fields of the returned struct are necessarilly filled,
++ * unset fields contain a 0 length string.
++ *
++ * \param config        pointer to a libiscsi_network_config struct to fill.
++ *
++ * \return              0 on success, ENODEV when no iscsi firmware was found.
++ */
++PUBLIC int libiscsi_get_firmware_network_config(
++    struct libiscsi_network_config *config);
++
++/** \brief Get the initiator name (iqn) from the iscsi firmware
++ *
++ * Get the initiator name (iqn) from the iscsi firmware.
++ *
++ * \param initiatorname The initiator name is stored here, this buffer must be
++ *                      atleast LIBISCSI_VALUE_MAXLEN bytes large.
++ * \return              0 on success, ENODEV when no iscsi firmware was found.
++ */
++PUBLIC int libiscsi_get_firmware_initiator_name(char *initiatorname);
++
++#undef PUBLIC
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif
+diff --git a/libiscsi/no_date_footer.html b/libiscsi/no_date_footer.html
+new file mode 100644
+index 0000000..1e0c6c4
+--- /dev/null
++++ b/libiscsi/no_date_footer.html
+@@ -0,0 +1,6 @@
++<hr size="1"><address style="text-align: right;"><small>
++Generated for $projectname by <a href="http://www.doxygen.org/
++index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a>
++$doxygenversion</small></address>
++</body>
++</html>
+diff --git a/libiscsi/pylibiscsi.c b/libiscsi/pylibiscsi.c
+new file mode 100644
+index 0000000..8800853
+--- /dev/null
++++ b/libiscsi/pylibiscsi.c
+@@ -0,0 +1,709 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede at redhat.com>
++ * maintained by open-iscsi at googlegroups.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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <Python.h>
++#include "libiscsi.h"
++
++#if PY_MAJOR_VERSION >= 3
++#define IS_PY3K
++#define MODINITERROR return NULL
++#define PYNUM_FROMLONG PyLong_FromLong
++#define PYSTR_FROMSTRING PyUnicode_FromString
++#else
++#define MODINITERROR return
++#define PYNUM_FROMLONG PyInt_FromLong
++#define PYSTR_FROMSTRING PyString_FromString
++#endif
++
++#define RET_TRUE_ELSE_FALSE { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; }
++#define CMP_TO_RICHCMP(cmpfunc) \
++	int comp_res = cmpfunc(self, other); \
++	switch (op) { \
++	    case Py_LT: \
++		if (comp_res < 0) RET_TRUE_ELSE_FALSE \
++	    case Py_LE: \
++		if (comp_res <= 0) RET_TRUE_ELSE_FALSE \
++	    case Py_EQ: \
++		if (comp_res == 0) RET_TRUE_ELSE_FALSE \
++	    case Py_NE: \
++		if (comp_res != 0) RET_TRUE_ELSE_FALSE \
++	    case Py_GT: \
++		if (comp_res > 0) RET_TRUE_ELSE_FALSE \
++	    default: \
++		if (comp_res >= 0) RET_TRUE_ELSE_FALSE \
++	}
++
++static struct libiscsi_context *context = NULL;
++
++/****************************** helpers ***********************************/
++static int check_string(const char *string)
++{
++	if (strlen(string) >= LIBISCSI_VALUE_MAXLEN) {
++		PyErr_SetString(PyExc_ValueError, "string too long");
++		return -1;
++	}
++	return 0;
++}
++
++/********************** PyIscsiChapAuthInfo ***************************/
++
++typedef struct {
++	PyObject_HEAD
++
++	struct libiscsi_auth_info info;
++} PyIscsiChapAuthInfo;
++
++static int PyIscsiChapAuthInfo_init(PyObject *self, PyObject *args,
++				    PyObject *kwds)
++{
++	int i;
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	char *kwlist[] = {"username", "password", "reverse_username",
++				"reverse_password", NULL};
++	const char *string[4] = { NULL, NULL, NULL, NULL };
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds,
++					"zz|zz:chapAuthInfo.__init__",
++					kwlist, &string[0], &string[1],
++					&string[2], &string[3]))
++		return -1;
++
++	for (i = 0; i < 4; i++)
++		if (string[i] && check_string(string[i]))
++			return -1;
++
++	memset (&chap->info, 0, sizeof(chap->info));
++	chap->info.method = libiscsi_auth_chap;
++	if (string[0])
++		strcpy(chap->info.chap.username, string[0]);
++	if (string[1])
++		strcpy(chap->info.chap.password, string[1]);
++	if (string[2])
++		strcpy(chap->info.chap.reverse_username, string[2]);
++	if (string[3])
++		strcpy(chap->info.chap.reverse_password, string[3]);
++
++	if (libiscsi_verify_auth_info(context, &chap->info)) {
++		PyErr_SetString(PyExc_ValueError,
++				libiscsi_get_error_string(context));
++		return -1;
++	}
++	return 0;
++}
++
++static PyObject *PyIscsiChapAuthInfo_get(PyObject *self, void *data)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	const char *attr = (const char *)data;
++
++	if (!strcmp(attr, "username")) {
++		return PYSTR_FROMSTRING(chap->info.chap.username);
++	} else if (!strcmp(attr, "password")) {
++		return PYSTR_FROMSTRING(chap->info.chap.password);
++	} else if (!strcmp(attr, "reverse_username")) {
++		return PYSTR_FROMSTRING(chap->info.chap.reverse_username);
++	} else if (!strcmp(attr, "reverse_password")) {
++		return PYSTR_FROMSTRING(chap->info.chap.reverse_password);
++	}
++	return NULL;
++}
++
++static int PyIscsiChapAuthInfo_set(PyObject *self, PyObject *value, void *data)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	const char *attr = (const char *)data;
++	const char *str;
++
++	if (!PyArg_Parse(value, "s", &str) || check_string(str))
++		return -1;
++
++	if (!strcmp(attr, "username")) {
++		strcpy(chap->info.chap.username, str);
++	} else if (!strcmp(attr, "password")) {
++		strcpy(chap->info.chap.password, str);
++	} else if (!strcmp(attr, "reverse_username")) {
++		strcpy(chap->info.chap.reverse_username, str);
++	} else if (!strcmp(attr, "reverse_password")) {
++		strcpy(chap->info.chap.reverse_password, str);
++	}
++
++	return 0;
++}
++
++static int PyIscsiChapAuthInfo_compare(PyIscsiChapAuthInfo *self,
++				       PyIscsiChapAuthInfo *other)
++{
++	int r;
++
++	r = strcmp(self->info.chap.username, other->info.chap.username);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.password, other->info.chap.password);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.reverse_username,
++		   other->info.chap.reverse_username);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.reverse_password,
++		   other->info.chap.reverse_password);
++	return r;
++}
++
++PyObject *PyIscsiChapAuthInfo_richcompare(PyIscsiChapAuthInfo *self,
++	                                  PyIscsiChapAuthInfo *other,
++					  int op)
++{
++	CMP_TO_RICHCMP(PyIscsiChapAuthInfo_compare)
++}
++
++static PyObject *PyIscsiChapAuthInfo_str(PyObject *self)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	char s[1024], reverse[512] = "";
++
++	if (chap->info.chap.reverse_username[0])
++		snprintf(reverse, sizeof(reverse), ", %s:%s",
++			 chap->info.chap.reverse_username,
++			 chap->info.chap.reverse_password);
++
++	snprintf(s, sizeof(s), "%s:%s%s", chap->info.chap.username,
++		 chap->info.chap.password, reverse);
++
++	return PYSTR_FROMSTRING(s);
++}
++
++static struct PyGetSetDef PyIscsiChapAuthInfo_getseters[] = {
++	{"username", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"username", "username"},
++	{"password", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"password", "password"},
++	{"reverse_username", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"reverse_username", "reverse_username"},
++	{"reverse_password", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"reverse_password", "reverse_password"},
++	{NULL}
++};
++
++PyTypeObject PyIscsiChapAuthInfo_Type = {
++	PyVarObject_HEAD_INIT(NULL, 0)
++	.tp_name = "libiscsi.chapAuthInfo",
++	.tp_basicsize = sizeof (PyIscsiChapAuthInfo),
++	.tp_getset = PyIscsiChapAuthInfo_getseters,
++	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
++#ifndef IS_PY3K
++	// Py_TPFLAGS_CHECKTYPES is only needed on Python 2
++	|  Py_TPFLAGS_CHECKTYPES
++#endif
++	,
++	.tp_richcompare = (richcmpfunc)PyIscsiChapAuthInfo_compare,
++	.tp_init = PyIscsiChapAuthInfo_init,
++	.tp_str = PyIscsiChapAuthInfo_str,
++	.tp_new = PyType_GenericNew,
++	.tp_doc = "iscsi chap authentication information.",
++};
++
++/***************************** PyIscsiNode  ********************************/
++
++typedef struct {
++	PyObject_HEAD
++
++	struct libiscsi_node node;
++} PyIscsiNode;
++
++static int PyIscsiNode_init(PyObject *self, PyObject *args, PyObject *kwds)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	char *kwlist[] = {"name", "tpgt", "address", "port", "iface", NULL};
++	const char *name = NULL, *address = NULL, *iface = NULL;
++	int tpgt = -1, port = 3260;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|isis:node.__init__",
++					 kwlist, &name, &tpgt, &address,
++					 &port, &iface))
++		return -1;
++	if (address == NULL) {
++		PyErr_SetString(PyExc_ValueError, "address not set");
++		return -1;
++	}
++	if (check_string(name) || check_string(address) || check_string(iface))
++		return -1;
++
++	strcpy(node->node.name, name);
++	node->node.tpgt = tpgt;
++	strcpy(node->node.address, address);
++	node->node.port = port;
++	strcpy(node->node.iface, iface);
++
++	return 0;
++}
++
++static PyObject *PyIscsiNode_get(PyObject *self, void *data)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *attr = (const char *)data;
++
++	if (!strcmp(attr, "name")) {
++		return PYSTR_FROMSTRING(node->node.name);
++	} else if (!strcmp(attr, "tpgt")) {
++		return PYNUM_FROMLONG(node->node.tpgt);
++	} else if (!strcmp(attr, "address")) {
++		return PYSTR_FROMSTRING(node->node.address);
++	} else if (!strcmp(attr, "port")) {
++		return PYNUM_FROMLONG(node->node.port);
++	} else if (!strcmp(attr, "iface")) {
++		return PYSTR_FROMSTRING(node->node.iface);
++	}
++	return NULL;
++}
++
++static int PyIscsiNode_set(PyObject *self, PyObject *value, void *data)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *attr = (const char *)data;
++	const char *str;
++	int i;
++
++	if (!strcmp(attr, "name")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.name, str);
++	} else if (!strcmp(attr, "tpgt")) {
++		if (!PyArg_Parse(value, "i", &i))
++			return -1;
++		node->node.tpgt = i;
++	} else if (!strcmp(attr, "address")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.address, str);
++	} else if (!strcmp(attr, "port")) {
++		if (!PyArg_Parse(value, "i", &i))
++			return -1;
++		node->node.port = i;
++	} else if (!strcmp(attr, "iface")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.iface, str);
++	}
++
++	return 0;
++}
++
++static int PyIscsiNode_compare(PyIscsiNode *self, PyIscsiNode *other)
++{
++	int res;
++
++	res = strcmp(self->node.name, other->node.name);
++	if (res)
++		return res;
++
++	if (self->node.tpgt < other->node.tpgt)
++		return -1;
++	if (self->node.tpgt > other->node.tpgt)
++		return -1;
++
++	res = strcmp(self->node.address, other->node.address);
++	if (res)
++		return res;
++
++	if (self->node.port < other->node.port)
++		return -1;
++	if (self->node.port > other->node.port)
++		return -1;
++
++	res = strcmp(self->node.iface, other->node.iface);
++	if (res)
++		return res;
++
++	return 0;
++}
++
++PyObject *PyIscsiNode_richcompare(PyIscsiNode *self, PyIscsiNode *other, int op)
++{
++    CMP_TO_RICHCMP(PyIscsiNode_compare)
++}
++
++static PyObject *PyIscsiNode_str(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	char s[1024], tpgt[16] = "";
++
++	if (node->node.tpgt != -1)
++		sprintf(tpgt, ",%d", node->node.tpgt);
++
++	snprintf(s, sizeof(s), "%s:%d%s %s", node->node.address,
++		 node->node.port, tpgt, node->node.name);
++
++	return PYSTR_FROMSTRING(s);
++}
++
++static PyObject *PyIscsiNode_login(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++
++	if (libiscsi_node_login(context, &node->node)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_logout(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++
++	if (libiscsi_node_logout(context, &node->node)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_setAuth(PyObject *self, PyObject *args,
++				     PyObject *kwds)
++{
++	char *kwlist[] = {"authinfo", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	PyObject *arg;
++	const struct libiscsi_auth_info *authinfo = NULL;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &arg))
++		return NULL;
++
++	if (arg == Py_None) {
++		authinfo = NULL;
++	} else if (PyObject_IsInstance(arg, (PyObject *)
++				       &PyIscsiChapAuthInfo_Type)) {
++		PyIscsiChapAuthInfo *pyauthinfo = (PyIscsiChapAuthInfo *)arg;
++		authinfo = &pyauthinfo->info;
++	} else {
++		PyErr_SetString(PyExc_ValueError, "invalid authinfo type");
++		return NULL;
++	}
++
++	if (libiscsi_node_set_auth(context, &node->node, authinfo)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_getAuth(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	PyIscsiChapAuthInfo *pyauthinfo;
++	struct libiscsi_auth_info authinfo;
++
++	if (libiscsi_node_get_auth(context, &node->node, &authinfo)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	switch (authinfo.method) {
++	case libiscsi_auth_chap:
++		pyauthinfo = PyObject_New(PyIscsiChapAuthInfo,
++					  &PyIscsiChapAuthInfo_Type);
++		if (!pyauthinfo)
++			return NULL;
++
++		pyauthinfo->info = authinfo;
++
++		return (PyObject *)pyauthinfo;
++
++	case libiscsi_auth_none:
++	default:
++		Py_RETURN_NONE;
++	}
++}
++
++static PyObject *PyIscsiNode_setParameter(PyObject *self, PyObject *args,
++					  PyObject *kwds)
++{
++	char *kwlist[] = {"parameter", "value", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *parameter, *value;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
++					 &parameter, &value))
++		return NULL;
++	if (check_string(parameter) || check_string(value))
++		return NULL;
++
++	if (libiscsi_node_set_parameter(context, &node->node, parameter,
++				        value)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_getParameter(PyObject *self, PyObject *args,
++					  PyObject *kwds)
++{
++	char *kwlist[] = {"parameter", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *parameter;
++	char value[LIBISCSI_VALUE_MAXLEN];
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &parameter))
++		return NULL;
++	if (check_string(parameter))
++		return NULL;
++
++	if (libiscsi_node_get_parameter(context, &node->node, parameter,
++					value)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	return Py_BuildValue("s", value);
++}
++
++static struct PyGetSetDef PyIscsiNode_getseters[] = {
++	{"name", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"name", "name"},
++	{"tpgt", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"tpgt", "tpgt"},
++	{"address", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"address", "address"},
++	{"port", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"port", "port"},
++	{"iface", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"iface", "iface"},
++	{NULL}
++};
++
++static struct PyMethodDef  PyIscsiNode_methods[] = {
++	{"login", (PyCFunction) PyIscsiNode_login, METH_NOARGS,
++		"Log in to the node"},
++	{"logout", (PyCFunction) PyIscsiNode_logout, METH_NOARGS,
++		"Log out of the node"},
++	{"setAuth", (PyCFunction) PyIscsiNode_setAuth,
++		METH_VARARGS|METH_KEYWORDS,
++		"Set authentication information"},
++	{"getAuth", (PyCFunction) PyIscsiNode_getAuth, METH_NOARGS,
++		"Get authentication information"},
++	{"setParameter", (PyCFunction) PyIscsiNode_setParameter,
++		METH_VARARGS|METH_KEYWORDS,
++		"Set an iscsi node parameter"},
++	{"getParameter", (PyCFunction) PyIscsiNode_getParameter,
++		METH_VARARGS|METH_KEYWORDS,
++		"Get an iscsi node parameter"},
++	{NULL}
++};
++
++PyTypeObject PyIscsiNode_Type = {
++	PyVarObject_HEAD_INIT(NULL, 0)
++	.tp_name = "libiscsi.node",
++	.tp_basicsize = sizeof (PyIscsiNode),
++	.tp_getset = PyIscsiNode_getseters,
++	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
++#ifndef IS_PY3K
++	| Py_TPFLAGS_CHECKTYPES
++#endif
++	,
++	.tp_methods = PyIscsiNode_methods,
++	.tp_richcompare = (richcmpfunc)PyIscsiNode_richcompare,
++	.tp_init = PyIscsiNode_init,
++	.tp_str = PyIscsiNode_str,
++	.tp_new = PyType_GenericNew,
++	.tp_doc = "The iscsi node contains iscsi node information.",
++};
++
++/***************************************************************************/
++
++static PyObject *pylibiscsi_discover_sendtargets(PyObject *self,
++						PyObject *args, PyObject *kwds)
++{
++	char *kwlist[] = {"address", "port", "authinfo", NULL};
++	const char *address = NULL;
++	int i, nr_found, port = 3260;
++	PyObject *authinfo_arg = NULL;
++	const struct libiscsi_auth_info *authinfo = NULL;
++	struct libiscsi_node *found_nodes;
++	PyObject* found_node_list;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iO",
++					kwlist, &address, &port,
++					&authinfo_arg))
++		return NULL;
++
++	if (authinfo_arg) {
++		if (PyObject_IsInstance(authinfo_arg, (PyObject *)
++					       &PyIscsiChapAuthInfo_Type)) {
++			PyIscsiChapAuthInfo *pyauthinfo =
++				(PyIscsiChapAuthInfo *)authinfo_arg;
++			authinfo = &pyauthinfo->info;
++		} else if (authinfo_arg != Py_None) {
++			PyErr_SetString(PyExc_ValueError,
++				"invalid authinfo type");
++			return NULL;
++		}
++	}
++
++	if (libiscsi_discover_sendtargets(context, address, port, authinfo,
++					  &nr_found, &found_nodes)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	if (nr_found == 0)
++		Py_RETURN_NONE;
++
++	found_node_list = PyList_New(nr_found);
++	if (!found_node_list)
++		return NULL;
++
++	for(i = 0; i < nr_found; i++) {
++		PyIscsiNode *pynode;
++		
++		pynode = PyObject_New(PyIscsiNode, &PyIscsiNode_Type);
++		if (!pynode) {
++			/* This will deref already added nodes for us */
++			Py_DECREF(found_node_list);
++			return NULL;
++		}
++		pynode->node = found_nodes[i];
++		PyList_SET_ITEM(found_node_list, i, (PyObject *)pynode);
++	}
++
++	return found_node_list;	
++}
++
++static PyObject *pylibiscsi_discover_firmware(PyObject *self)
++{
++	int i, nr_found;
++	struct libiscsi_node *found_nodes;
<Skipped 38590 lines>
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/open-iscsi.git/commitdiff/3de50fc5e99b5643efe9c051d2892c3800666e04



More information about the pld-cvs-commit mailing list