[Debian-ha-commits] [libqb] 01/02: Imported Upstream version 0.17.1

Richard Winters devrik-guest at moszumanska.debian.org
Thu Apr 16 07:06:27 UTC 2015


This is an automated email from the git hooks/post-receive script.

devrik-guest pushed a commit to branch master
in repository libqb.

commit 476876e2cd1957f1ee03ce1277f0600c478983ad
Author: Richard B Winters <rik at mmogp.com>
Date:   Thu Apr 16 02:59:16 2015 -0400

    Imported Upstream version 0.17.1
---
 .gitignore                    |   29 +
 .travis.yml                   |   13 +
 COPYING                       |  458 ++++++++++++
 INSTALL                       |   58 ++
 Lindent                       |   29 +
 Makefile.am                   |  138 ++++
 README.markdown               |   49 ++
 autogen.sh                    |    4 +
 build-aux/.gitignore          |    7 +
 build-aux/abi-check-templ.xml |   12 +
 build-aux/api-auto-test.xml   |    9 +
 build-aux/git-version-gen     |  172 +++++
 build-aux/gitlog-to-changelog |  191 +++++
 build-aux/release.mk          |   75 ++
 check                         |  334 +++++++++
 coding_style.txt              |   32 +
 configure.ac                  |  620 +++++++++++++++++
 docs/.gitignore               |    3 +
 docs/Makefile.am              |   48 ++
 docs/html.dox.in              |  213 ++++++
 docs/mainpage.h               |  117 ++++
 docs/man.dox.in               |  161 +++++
 docs/man8/qb-blackbox.8       |   59 ++
 examples/.gitignore           |    6 +
 examples/Makefile.am          |   51 ++
 examples/ipcclient.c          |  241 +++++++
 examples/ipcserver.c          |  389 +++++++++++
 examples/mapnotify.c          |  210 ++++++
 examples/simplelog.c          |  256 +++++++
 examples/tcpclient.c          |   91 +++
 examples/tcpserver.c          |  154 ++++
 include/.gitignore            |    5 +
 include/Makefile.am           |   27 +
 include/os_base.h             |  182 +++++
 include/qb/Makefile.am        |   26 +
 include/qb/qbarray.h          |  128 ++++
 include/qb/qbatomic.h         |  207 ++++++
 include/qb/qbconfig.h.in      |   31 +
 include/qb/qbdefs.h           |  108 +++
 include/qb/qbhdb.h            |  178 +++++
 include/qb/qbipc_common.h     |   66 ++
 include/qb/qbipcc.h           |  257 +++++++
 include/qb/qbipcs.h           |  474 +++++++++++++
 include/qb/qblist.h           |  314 +++++++++
 include/qb/qblog.h            |  642 +++++++++++++++++
 include/qb/qbloop.h           |  289 ++++++++
 include/qb/qbmap.h            |  284 ++++++++
 include/qb/qbrb.h             |  304 ++++++++
 include/qb/qbutil.h           |  298 ++++++++
 include/tlist.h               |  218 ++++++
 lib/.gitignore                |    1 +
 lib/Makefile.am               |   86 +++
 lib/array.c                   |  230 ++++++
 lib/atomic_int.h              |  131 ++++
 lib/hashtable.c               |  523 ++++++++++++++
 lib/hdb.c                     |  291 ++++++++
 lib/ipc_int.h                 |  207 ++++++
 lib/ipc_setup.c               |  728 +++++++++++++++++++
 lib/ipc_shm.c                 |  368 ++++++++++
 lib/ipc_socket.c              |  780 +++++++++++++++++++++
 lib/ipcc.c                    |  451 ++++++++++++
 lib/ipcs.c                    |  965 +++++++++++++++++++++++++
 lib/libqb.pc.in               |   11 +
 lib/log.c                     | 1077 ++++++++++++++++++++++++++++
 lib/log_blackbox.c            |  297 ++++++++
 lib/log_dcs.c                 |  211 ++++++
 lib/log_file.c                |  116 +++
 lib/log_format.c              |  842 ++++++++++++++++++++++
 lib/log_int.h                 |  108 +++
 lib/log_syslog.c              |   78 +++
 lib/log_thread.c              |  279 ++++++++
 lib/loop.c                    |  218 ++++++
 lib/loop_int.h                |  108 +++
 lib/loop_job.c                |  168 +++++
 lib/loop_poll.c               |  780 +++++++++++++++++++++
 lib/loop_poll_epoll.c         |  205 ++++++
 lib/loop_poll_int.h           |   91 +++
 lib/loop_poll_kqueue.c        |  212 ++++++
 lib/loop_poll_poll.c          |  109 +++
 lib/loop_timerlist.c          |  287 ++++++++
 lib/map.c                     |  131 ++++
 lib/map_int.h                 |   75 ++
 lib/ringbuffer.c              |  960 +++++++++++++++++++++++++
 lib/ringbuffer_helper.c       |  308 ++++++++
 lib/ringbuffer_int.h          |   98 +++
 lib/rpl_sem.c                 |  214 ++++++
 lib/rpl_sem.h                 |   64 ++
 lib/skiplist.c                |  569 +++++++++++++++
 lib/strchrnul.c               |   35 +
 lib/strlcat.c                 |   42 ++
 lib/strlcpy.c                 |   41 ++
 lib/trie.c                    |  815 ++++++++++++++++++++++
 lib/unix.c                    |  494 +++++++++++++
 lib/util.c                    |  369 ++++++++++
 lib/util_int.h                |  119 ++++
 libqb.spec.in                 |   72 ++
 tests/.gitignore              |   15 +
 tests/Makefile.am             |  145 ++++
 tests/bench-log.c             |   96 +++
 tests/blackbox-segfault.sh    |   26 +
 tests/bmc.c                   |  198 ++++++
 tests/bmcpt.c                 |  188 +++++
 tests/bms.c                   |  361 ++++++++++
 tests/check_array.c           |  178 +++++
 tests/check_ipc.c             | 1549 +++++++++++++++++++++++++++++++++++++++++
 tests/check_log.c             |  769 ++++++++++++++++++++
 tests/check_loop.c            |  774 ++++++++++++++++++++
 tests/check_map.c             | 1010 +++++++++++++++++++++++++++
 tests/check_rb.c              |  232 ++++++
 tests/check_util.c            |  190 +++++
 tests/crash_test_dummy.c      |  108 +++
 tests/file_change_bytes.c     |  143 ++++
 tests/format_compare_speed.c  |   97 +++
 tests/loop.c                  |  125 ++++
 tests/make-log-test.sh        |   34 +
 tests/rbreader.c              |   75 ++
 tests/rbwriter.c              |  154 ++++
 tests/resources.test          |   22 +
 tests/test.conf.in            |    6 +
 tools/.gitignore              |    1 +
 tools/Makefile.am             |   28 +
 tools/qb_blackbox.c           |   34 +
 122 files changed, 29189 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..23c3507
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+*.[oa]
+*.deps
+*.so
+*.la
+*.lo
+*.so.*
+*.3
+*.rpm
+*.pc
+*.log
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+config.status
+configure
+.libs
+m4
+libtool
+.version
+.tarball-version
+libqb.spec
+libqb-*
+cov
+compat_reports
+abi_dumps
+TAGS
+*~
+test-driver
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..42bad2f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: c
+compiler:
+  - gcc
+before_install: sudo apt-get install check splint
+install:
+ # Deal with issue on Travis builders
+ # https://github.com/travis-ci/travis-cookbooks/issues/155
+ - "sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm"
+script: ./autogen.sh && ./configure && make check && make distcheck
+notifications:
+  email:
+    recipients:
+      - quarterback-devel at lists.fedorahosted.org
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e9ab0b3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,458 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+

+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+

+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..3fee769
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,58 @@
+----------------------------------------------
+The Quarterback library Installation Guide
+----------------------------------------------
+Please read LICENSE for a description of the licensing of this software.
+
+---------------------------------
+* Platforms Built and Tested On *
+---------------------------------
+Quarterback has been tested on:
+Linux: Fedora 12
+OpenSolaris
+BSD: FreeBSD
+MacOSX: Darwin latest update
+
+It should build and run properly on the tested platforms as well as possibly
+others with minimal effort.  Quarterback requires a coherent mmap() system call
+and will not operate on platforms which don't support coherent mmap().
+
+----------------------------
+* Building from git *
+----------------------------
+When building and installing from subversion, automake 2.61 or later is
+required.  Prior versions will result in build failures.
+
+Step 1: check out a copy of the repository
+git clone git://git.fedoraprojects.org/git/quarterback
+
+Find the version you want to build.  Usually this will be the "master"
+branch.  If you want to build a specific released
+version check 'git tag -l'.
+
+Step 2: Generate the makefiles
+balance: ./autogen.sh
+
+Step 3: Run the configure script
+Note that this will install to /usr by default to install to /usr/local use --prefix=/usr/local
+balance: ./configure
+
+Step 4: build
+balance: make
+
+Step 5: Install the binaries
+balance: sudo make install
+
+-------------------------
+* Building from tarball *
+-------------------------
+The tarball is distributed with pregenerated makefiles.  There is no need
+to run the autogen.sh script in this case.
+
+Step 1: Run the configure script
+balance:~/quarterback% ./configure
+
+Step 2: Install the binaries
+balance:~/quarterback% su
+balance:~/quarterback# make install
+
+
diff --git a/Lindent b/Lindent
new file mode 100755
index 0000000..0c39b28
--- /dev/null
+++ b/Lindent
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# This is a copy of the linux kernel's Lindent because
+# we use the same formatting. No point re-inventing that
+# wheel.
+#
+# Differences to kernel style:
+# --dont-break-procedure-type -> --break-procedure-type
+#
+PARAM="-npro -nbad -bap -nbc -bbo -hnl -br -brs -c33 -cd33 -ncdb -ce -ci4
+-cli0 -d0 -di1 -nfc1 -i8 -ip0 -l80 -lp -npcs -nprs -psl -sai
+-saf -saw -ncs -nsc -sob -nfca -cp1 -ss -ts8"
+RES=`indent --version`
+V1=`echo $RES | cut -d' ' -f3 | cut -d'.' -f1`
+V2=`echo $RES | cut -d' ' -f3 | cut -d'.' -f2`
+V3=`echo $RES | cut -d' ' -f3 | cut -d'.' -f3`
+if [ $V1 -gt 2 ]; then
+	PARAM="$PARAM -il0"
+elif [ $V1 -eq 2 ]; then
+	if [ $V2 -gt 2 ]; then
+		PARAM="$PARAM -il0";
+	elif [ $V2 -eq 2 ]; then
+		if [ $V3 -ge 10 ]; then
+			PARAM="$PARAM -il0"
+		fi
+	fi
+fi
+indent $PARAM "$@"
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..b918801
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,138 @@
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# Authors: Andrew Beekhof <abeekhof at redhat.com>
+#	   Steven Dake <sdake at redhat.com>
+#	   Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+
+SPEC			= $(PACKAGE_NAME).spec
+
+TARFILE			= $(PACKAGE_NAME)-$(VERSION).tar.gz
+
+EXTRA_DIST		= autogen.sh $(SPEC).in \
+			  build-aux/git-version-gen \
+			  build-aux/gitlog-to-changelog \
+			  build-aux/release.mk \
+			  .version
+
+AUTOMAKE_OPTIONS	= foreign
+
+MAINTAINERCLEANFILES	= Makefile.in aclocal.m4 configure depcomp \
+			  config.guess config.sub missing install-sh \
+			  autoheader automake autoconf libtool libtoolize \
+			  ltmain.sh compile build-aux/test-driver
+
+
+ACLOCAL_AMFLAGS		= -I m4
+
+dist_doc_DATA		= COPYING INSTALL README.markdown
+
+SUBDIRS			= include lib docs tools tests examples
+
+doxygen:
+	$(MAKE) -C docs doxygen
+
+dist-clean-local:
+	$(AM_V_GEN)rm -f autoconf automake autoheader
+
+maintainer-clean-local:
+	$(AM_V_GEN)rm -rf m4
+	$(AM_V_GEN)rm -f .version .tarball-version
+
+clean-generic:
+	$(AM_V_GEN)rm -rf $(SPEC) $(TARFILE)
+
+## make rpm/srpm section.
+
+$(SPEC): $(SPEC).in
+	$(AM_V_GEN)rm -f $@-t $@
+	LC_ALL=C date="$(shell date "+%a %b %d %Y")" && \
+	if [ -f .tarball-version ]; then \
+		gitver="$(shell cat .tarball-version)" && \
+		rpmver=$$gitver && \
+		alphatag="" && \
+		dirty="" && \
+		numcomm="0"; \
+	else \
+		gitver="$(shell git describe --abbrev=4 --match='v*' --tags HEAD 2>/dev/null)" && \
+		rpmver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \
+		alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \
+		vtag=`echo $$gitver | sed -e "s/-.*//g"` && \
+		numcomm=`git rev-list $$vtag..HEAD | wc -l` && \
+		git update-index --refresh > /dev/null 2>&1 || true && \
+		dirty=`git diff-index --name-only HEAD 2>/dev/null`; \
+	fi && \
+	if [ -n "$$dirty" ]; then dirty="dirty"; else dirty=""; fi && \
+	if [ "$$numcomm" = "0" ]; then \
+		sed \
+			-e "s#@version@#$$rpmver#g" \
+			-e "s#%glo.*alpha.*##g" \
+			-e "s#%glo.*numcomm.*##g" \
+			-e "s#@dirty@#$$dirty#g" \
+			-e "s#@date@#$$date#g" \
+		$< > $@-t; \
+	else \
+		sed \
+			-e "s#@version@#$$rpmver#g" \
+			-e "s#@alphatag@#$$alphatag#g" \
+			-e "s#@numcomm@#$$numcomm#g" \
+			-e "s#@dirty@#$$dirty#g" \
+			-e "s#@date@#$$date#g" \
+		$< > $@-t; \
+	fi; \
+	if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi
+	$(AM_V_GEN)chmod a-w $@-t
+	$(AM_V_GEN)mv $@-t $@
+
+$(TARFILE):
+	$(MAKE) dist
+
+RPMBUILDOPTS	= --define "_sourcedir $(abs_builddir)" \
+		  --define "_specdir $(abs_builddir)" \
+		  --define "_builddir $(abs_builddir)" \
+		  --define "_srcrpmdir $(abs_builddir)" \
+		  --define "_rpmdir $(abs_builddir)"
+
+tarball: $(TARFILE)
+
+srpm: clean
+	autoreconf -if
+	$(MAKE) $(SPEC) tarball
+	rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC)
+
+rpm: clean
+	autoreconf -if
+	$(MAKE) $(SPEC) tarball
+	rpmbuild $(RPMBUILDOPTS) -ba $(SPEC)
+
+# release/versioning
+BUILT_SOURCES	= .version
+.version:
+	echo $(VERSION) > $@-t && mv $@-t $@
+
+dist-hook: gen-ChangeLog
+	echo $(VERSION) > $(distdir)/.tarball-version
+
+gen_start_date = 2000-01-01
+.PHONY: gen-ChangeLog
+gen-ChangeLog:
+	if test -d .git; then						\
+		$(top_srcdir)/build-aux/gitlog-to-changelog		\
+			--since=$(gen_start_date) > $(distdir)/cl-t;	\
+		rm -f $(distdir)/ChangeLog;				\
+		mv $(distdir)/cl-t $(distdir)/ChangeLog;		\
+	fi
diff --git a/README.markdown b/README.markdown
new file mode 100644
index 0000000..b8bedc0
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,49 @@
+# libqb
+
+## What is libqb?
+libqb is a library with the primary purpose of providing high performance
+client server reusable features. It provides high performance logging,
+tracing, ipc, and poll.
+
+We don't intend be an all encompassing library, but instead provide very
+specially focused APIs that are highly tuned for maximum performance for client/server applications.
+
+[![Build Status](https://travis-ci.org/ClusterLabs/libqb.png)](https://travis-ci.org/ClusterLabs/libqb)
+
+## For more information look at:
+* [Our wiki](https://github.com/clusterlabs/libqb/wiki)
+* [Issues/Bugs](https://github.com/clusterlabs/libqb/issues)
+* [The doxygen generated manual](http://clusterlabs.github.io/libqb/0.16.0/doxygen/)
+* You can build it yourself with the following commands:
+
+    $ make doxygen
+    $ firefox ./doc/html/index.html
+
+## Dependencies
+* glib-2.0-devel (If you want to build the glib example code)
+* check-devel (If you want to run the tests)
+* doxygen and graphviz (If you want to build the doxygen man pages or html manual)
+
+## Source Control (GIT)
+
+    git clone git://github.com/ClusterLabs/libqb.git
+
+[See Github](https://github.com/clusterlabs/libqb)
+
+## Installing from source
+
+    $ ./autogen.sh
+    $ ./configure
+    $ make
+    $ sudo make install
+
+## How you can help
+If you find this project useful, you may want to consider supporting its future development.
+There are a number of ways to support the project.
+
+* Test and report issues.
+* Help others on the [mailing list](https://fedorahosted.org/mailman/listinfo/quarterback-devel).
+* Contribute documentation, examples and test cases.
+* Contribute patches.
+* Spread the word.
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..3c5e1d9
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+mkdir -p m4
+autoreconf -i -v && echo Now run ./configure and make
diff --git a/build-aux/.gitignore b/build-aux/.gitignore
new file mode 100644
index 0000000..22646bc
--- /dev/null
+++ b/build-aux/.gitignore
@@ -0,0 +1,7 @@
+compile
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
diff --git a/build-aux/abi-check-templ.xml b/build-aux/abi-check-templ.xml
new file mode 100644
index 0000000..4cac826
--- /dev/null
+++ b/build-aux/abi-check-templ.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<descriptor>
+<version>
+    @VERSION@
+</version>
+<headers>
+	@PREFIX@/usr/include/qb
+</headers>
+<libs>
+	@PREFIX@/usr/lib64/libqb.so
+</libs>
+</descriptor>
diff --git a/build-aux/api-auto-test.xml b/build-aux/api-auto-test.xml
new file mode 100644
index 0000000..e89b780
--- /dev/null
+++ b/build-aux/api-auto-test.xml
@@ -0,0 +1,9 @@
+<version>
+    master
+</version>
+<headers>
+    include/qb
+</headers>
+<libs>
+    lib/
+</libs>
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
new file mode 100755
index 0000000..4539bd9
--- /dev/null
+++ b/build-aux/git-version-gen
@@ -0,0 +1,172 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2011-02-19.19; # UTC
+
+# Copyright (C) 2007-2011 Free Software Foundation, Inc.
+#
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project at example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#	echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#	echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+    1|2) ;;
+    *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version" \
+         '[TAG-NORMALIZATION-SED-SCRIPT]'
+       exit 1;;
+esac
+
+tarball_version_file=$1
+tag_sed_script="${2:-s/x/x/}"
+nl='
+'
+
+# Avoid meddling by environment variable of the same name.
+v=
+v_from_git=
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+    v=`cat $tarball_version_file` || v=
+    case $v in
+        *$nl*) v= ;; # reject multi-line output
+        [0-9]*) ;;
+        *) v= ;;
+    esac
+    test -z "$v" \
+        && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+    : # use $v
+# Otherwise, if there is at least one git commit involving the working
+# directory, and "git describe" output looks sensible, use that to
+# derive a version string.
+elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
+    && v=`git describe --abbrev=4 --match='v*' --tags HEAD 2>/dev/null \
+          || git describe --abbrev=4 --tags HEAD 2>/dev/null` \
+    && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
+    && case $v in
+         v[0-9]*) ;;
+         *) (exit 1) ;;
+       esac
+then
+    # Is this a new git that lists number of commits since the last
+    # tag or the previous older version that did not?
+    #   Newer: v6.10-77-g0f8faeb
+    #   Older: v6.10-g0f8faeb
+    case $v in
+        *-*-*) : git describe is okay three part flavor ;;
+        *-*)
+            : git describe is older two part flavor
+            # Recreate the number of commits and rewrite such that the
+            # result is the same as if we were using the newer version
+            # of git describe.
+            vtag=`echo "$v" | sed 's/-.*//'`
+            commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
+                || { commit_list=failed;
+                     echo "$0: WARNING: git rev-list failed" 1>&2; }
+            numcommits=`echo "$commit_list" | wc -l`
+            v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+            test "$commit_list" = failed && v=UNKNOWN
+            ;;
+    esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+    v_from_git=1
+else
+    v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Test whether to append the "-dirty" suffix only if the version
+# string we're using came from git.  I.e., skip the test if it's "UNKNOWN"
+# or if it came from .tarball-version.
+if test -n "$v_from_git"; then
+  # Don't declare a version "dirty" merely because a time stamp has changed.
+  git update-index --refresh > /dev/null 2>&1
+
+  dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
+  case "$dirty" in
+      '') ;;
+      *) # Append the suffix only if there isn't one already.
+          case $v in
+            *-dirty) ;;
+            *) v="$v-dirty" ;;
+          esac ;;
+  esac
+fi
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d "$nl"
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog
new file mode 100755
index 0000000..7660af5
--- /dev/null
+++ b/build-aux/gitlog-to-changelog
@@ -0,0 +1,191 @@
+eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
+  & eval 'exec perl -wS "$0" $argv:q'
+    if 0;
+# Convert git log output to ChangeLog format.
+
+my $VERSION = '2009-10-30 13:46'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job.  Otherwise, update this string manually.
+
+# Copyright (C) 2008-2010 Free Software Foundation, Inc.
+
+# 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 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# use File::Coda; # http://meyering.net/code/Coda/
+END {
+  defined fileno STDOUT or return;
+  close STDOUT and return;
+  warn "$ME: failed to close standard output: $!\n";
+  $? ||= 1;
+}
+
+sub usage ($)
+{
+  my ($exit_code) = @_;
+  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+  if ($exit_code != 0)
+    {
+      print $STREAM "Try `$ME --help' for more information.\n";
+    }
+  else
+    {
+      print $STREAM <<EOF;
+Usage: $ME [OPTIONS] [ARGS]
+
+Convert git log output to ChangeLog format.  If present, any ARGS
+are passed to "git log".  To avoid ARGS being parsed as options to
+$ME, they may be preceded by '--'.
+
+OPTIONS:
+
+   --since=DATE convert only the logs since DATE;
+                  the default is to convert all log entries.
+   --format=FMT set format string for commit subject and body;
+                  see 'man git-log' for the list of format metacharacters;
+                  the default is '%s%n%b%n'
+
+   --help       display this help and exit
+   --version    output version information and exit
+
+EXAMPLE:
+
+  $ME --since=2008-01-01 > ChangeLog
+  $ME -- -n 5 foo > last-5-commits-to-branch-foo
+
+EOF
+    }
+  exit $exit_code;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new string.
+sub shell_quote($)
+{
+  my ($s) = @_;
+  if ($s =~ m![^\w+/.,-]!)
+    {
+      # Convert each single quote to '\''
+      $s =~ s/\'/\'\\\'\'/g;
+      # Then single quote the string.
+      $s = "'$s'";
+    }
+  return $s;
+}
+
+sub quoted_cmd(@)
+{
+  return join (' ', map {shell_quote $_} @_);
+}
+
+{
+  my $since_date = '1970-01-01 UTC';
+  my $format_string = '%s%n%b%n';
+  GetOptions
+    (
+     help => sub { usage 0 },
+     version => sub { print "$ME version $VERSION\n"; exit },
+     'since=s' => \$since_date,
+     'format=s' => \$format_string,
+    ) or usage 1;
+
+  my @cmd = (qw (git log --log-size), "--since=$since_date",
+             '--pretty=format:%ct  %an  <%ae>%n%n'.$format_string, @ARGV);
+  open PIPE, '-|', @cmd
+    or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n"
+            . "(Is your Git too old?  Version 1.5.1 or later is required.)\n");
+
+  my $prev_date_line = '';
+  while (1)
+    {
+      defined (my $in = <PIPE>)
+        or last;
+      $in =~ /^log size (\d+)$/
+        or die "$ME:$.: Invalid line (expected log size):\n$in";
+      my $log_nbytes = $1;
+
+      my $log;
+      my $n_read = read PIPE, $log, $log_nbytes;
+      $n_read == $log_nbytes
+        or die "$ME:$.: unexpected EOF\n";
+
+      my @line = split "\n", $log;
+      my $author_line = shift @line;
+      defined $author_line
+        or die "$ME:$.: unexpected EOF\n";
+      $author_line =~ /^(\d+)  (.*>)$/
+        or die "$ME:$.: Invalid line "
+          . "(expected date/author/email):\n$author_line\n";
+
+      my $date_line = sprintf "%s  $2\n", strftime ("%F", localtime ($1));
+      # If this line would be the same as the previous date/name/email
+      # line, then arrange not to print it.
+      if ($date_line ne $prev_date_line)
+        {
+          $prev_date_line eq ''
+            or print "\n";
+          print $date_line;
+        }
+      $prev_date_line = $date_line;
+
+      # Omit "Signed-off-by..." lines.
+      @line = grep !/^Signed-off-by: .*>$/, @line;
+
+      # If there were any lines
+      if (@line == 0)
+        {
+          warn "$ME: warning: empty commit message:\n  $date_line\n";
+        }
+      else
+        {
+          # Remove leading and trailing blank lines.
+          while ($line[0] =~ /^\s*$/) { shift @line; }
+          while ($line[$#line] =~ /^\s*$/) { pop @line; }
+
+          # Prefix each non-empty line with a TAB.
+          @line = map { length $_ ? "\t$_" : '' } @line;
+
+          print "\n", join ("\n", @line), "\n";
+        }
+
+      defined ($in = <PIPE>)
+        or last;
+      $in ne "\n"
+        and die "$ME:$.: unexpected line:\n$in";
+    }
+
+  close PIPE
+    or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
+  # FIXME-someday: include $PROCESS_STATUS in the diagnostic
+}
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "my $VERSION = '"
+# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "'; # UTC"
+# End:
diff --git a/build-aux/release.mk b/build-aux/release.mk
new file mode 100644
index 0000000..15e074f
--- /dev/null
+++ b/build-aux/release.mk
@@ -0,0 +1,75 @@
+# to build official release tarballs, handle tagging and publish.
+
+# signing key
+gpgsignkey=582A3454
+
+project=libqb
+
+all: checks setup tag tarballs sha256 sign
+
+checks:
+ifeq (,$(version))
+	@echo ERROR: need to define version=
+	@exit 1
+endif
+	@if [ ! -d .git ]; then \
+		echo This script needs to be executed from top level cluster git tree; \
+		exit 1; \
+	fi
+
+setup: checks
+	./autogen.sh
+	./configure
+	make maintainer-clean
+
+tag: setup ./tag-$(version)
+
+tag-$(version):
+ifeq (,$(release))
+	@echo Building test release $(version), no tagging
+else
+	git tag -a -m "v$(version) release" v$(version) HEAD
+	@touch $@
+endif
+
+tarballs: tag
+	./autogen.sh
+	./configure
+	make distcheck
+
+sha256: tarballs $(project)-$(version).sha256
+
+$(project)-$(version).sha256:
+ifeq (,$(release))
+	@echo Building test release $(version), no sha256
+else
+	sha256sum $(project)-$(version)*tar* | sort -k2 > $@
+endif
+
+sign: sha256 $(project)-$(version).sha256.asc
+
+$(project)-$(version).sha256.asc: $(project)-$(version).sha256
+ifeq (,$(gpgsignkey))
+	@echo No GPG signing key defined
+else
+ifeq (,$(release))
+	@echo Building test release $(version), no sign
+else
+	gpg --default-key $(gpgsignkey) \
+		--detach-sign \
+		--armor \
+		$<
+endif
+endif
+
+publish:
+ifeq (,$(release))
+	@echo Building test release $(version), no publishing!
+else
+	@echo CHANGEME git push --tags origin
+	@echo CHANGEME scp $(project)-$(version).* \
+		fedorahosted.org:$(project)
+endif
+
+clean:
+	rm -rf $(project)-* tag-*
diff --git a/check b/check
new file mode 100755
index 0000000..a4e841f
--- /dev/null
+++ b/check
@@ -0,0 +1,334 @@
+#!/bin/bash
+
+export CFLAGS="$CFLAGS"
+export MAKEFLAGS="$MAKEFLAGS --no-print-directory"
+
+help_all() {
+	echo 
+	echo "Usage: check <command>"
+	echo 
+	echo "Commands:"
+	echo 
+	echo "  ansi            Check using ansi compiler option"
+	echo "  sysv            Check using sys-v semaphores"
+	echo "  nosection       Check without gcc __attribute__(section)"
+	echo "  noepoll         Check using poll (not epoll)"
+	echo "  nogettime       Check without gettime()"
+	echo "  bsd             Check with a bsd-like config"
+	echo "  mac             Check with a mac/darwin-like config"
+	echo "  dist            do make distcheck"
+	echo "  rpm             Run rpmlint"
+	echo "  mock            Test doing a mock build"
+	echo "  coverity        Run coverity"
+	echo "  clang           Run clang-analyze"
+	echo "  abi             Check abi compatibility"
+	echo "  api_sanity      api sanity test"
+	echo 
+	echo "  help            This help"
+	echo 
+	exit 1
+}
+
+if [ $# -lt 1 ]
+then
+    help_all
+fi
+
+command=$1
+shift
+args="$@"
+
+if [ -n "$(git rev-parse)" ] ; then
+        perror "Must be inside a git repository to work"
+        exit 1
+fi
+
+up=$(git rev-parse --show-cdup)
+if [ "x$up" == "x" ] ; then
+	up="."
+fi
+cd $up
+
+set -e
+if [ ! -e build-aux/install-sh ]
+then
+	./autogen.sh
+fi
+if [ ! -e Makefile ]
+then
+	./configure --quiet
+fi
+
+check() {
+	options="$1 --enable-debug --enable-slow-tests --enable-fatal-warnings --quiet"
+	echo "./configure $options"
+	echo "ENV CFLAGS=\"$CFLAGS\""
+	echo "ENV MAKEFLAGS=\"$MAKEFLAGS\""
+	( ./configure $options )
+	make check
+	if [ $? -ne 0 ]
+	then
+		echo "======================"
+		echo failed: $1
+		echo "======================"
+		if [ -f tests/test-suite.log ]
+		then
+			cat tests/test-suite.log
+		fi
+		exit 1
+	fi
+}
+
+check_ansi() {
+	echo "checking ansi"
+	echo "==============="
+	check "--enable-ansi"
+}
+
+check_nosection() {
+	echo "checking nosection"
+	echo "======================="
+	# no __attribute__((section))
+	check "ac_cv_link_attribute_section=no"
+}
+
+check_sysv() {
+	# use sys-v semaphores
+	echo "checking sysv"
+	echo "======================="
+	ORIG_CFLAGS=$CFLAGS
+	export CFLAGS="$CFLAGS -DDISABLE_POSIX_THREAD_PROCESS_SHARED"
+	check
+	export CFLAGS=$ORIG_CFLAGS
+}
+
+check_nogettime() {
+	# no clock_gettime
+	echo "checking nogettime"
+	echo "======================="
+	check "ac_cv_func_clock_gettime=no"
+}
+
+check_noepoll() {
+	# no epoll
+	echo "checking noepoll"
+	echo "======================="
+	check "ac_cv_func_epoll_create1=no ac_cv_func_epoll_create=no"
+}
+
+check_bsd() {
+	# bsd-like
+	echo "checking bsd"
+	echo "======================="
+	ORIG_CFLAGS=$CFLAGS
+	check "ac_cv_func_sem_timedwait=no ac_cv_func_clock_gettime=no ac_cv_func_epoll_create1=no ac_cv_func_epoll_create=no"
+	export CFLAGS=$ORIG_CFLAGS
+}
+
+check_mac() {
+	# mac-like
+	echo "checking mac"
+	echo "======================="
+	ORIG_CFLAGS=$CFLAGS
+	export CFLAGS="$CFLAGS -DDISABLE_POSIX_THREAD_PROCESS_SHARED"
+	check "ac_cv_func_clock_gettime=no ac_cv_func_epoll_create1=no ac_cv_func_epoll_create=no ac_cv_link_attribute_section=no"
+	export CFLAGS=$ORIG_CFLAGS
+}
+
+check_dist() {
+	# normal configure with distcheck
+	echo "checking dist"
+	echo "======================"
+	set +e
+	./configure --quiet
+	make distcheck
+	set -e
+}
+
+check_rpm() {
+	echo "checking rpm building"
+	echo "======================"
+	set +e
+	make maintainer-clean
+	./autogen.sh
+	./configure --quiet
+	make rpm
+	echo
+	sudo rpm -Uvf --force libqb-*.rpm
+	echo rpmlint libqb
+	rpmlint libqb
+	echo rpmlint libqb-debuginfo
+	rpmlint libqb-debuginfo
+	echo rpmlint libqb-devel
+	rpmlint libqb-devel
+	set -e
+}
+
+
+check_mock() {
+	echo "checking mock building"
+	echo "======================"
+	set +e
+	make maintainer-clean
+	rm -f *.rpm
+	./autogen.sh
+	./configure --quiet
+	make srpm
+	mock --no-clean --rebuild *.src.rpm
+}
+
+check_coverity() {
+	echo "checking coverity"
+	echo "======================"
+	make clean
+	cov-build --dir=cov make
+	cov-analyze --dir cov \
+		--concurrency \
+		--all \
+		--aggressiveness-level high \
+		--security \
+		--wait-for-license
+	cov-format-errors --dir cov
+}
+
+check_clang() {
+	if [ ! -f /usr/libexec/clang-analyzer/scan-build/ccc-analyzer ]
+	then
+		echo try installing clang-analyze
+		exit 1
+	fi
+	echo "checking clang"
+	echo "===================="
+	make clean
+
+	./configure \
+		CC=/usr/libexec/clang-analyzer/scan-build/ccc-analyzer \
+		CXX=/usr/libexec/clang-analyzer/scan-build/c++-analyzer
+	make check
+}
+
+check_abi() {
+	ver1=$1
+	ver2=$2
+	if [ -z "$ver1" ] ; then
+		echo need two versions.
+		exit 1
+	fi
+	if [ -z "$ver2" ] ; then
+		echo need two versions.
+		exit 1
+	fi
+
+	TMPL=build-aux/abi-check-templ.xml
+	checker=abi-compliance-checker
+
+	mkdir -p abi_dumps/libqb
+	for v in $ver1 $ver2
+	do
+		p=$(pwd)_inst_$v
+		sed -e "s|@PREFIX@|$p|" -e "s|@VERSION@|$v|" $TMPL > abi_dumps/libqb/$v.xml
+	done
+	for v in $ver1 $ver2
+	do
+		p=$(pwd)_inst_$v
+		t=v$v
+		b=api-check-$v
+		echo "== Version $v =="
+		if [ ! -f abi_dumps/libqb/libqb_$v.abi.tar.gz ]
+		then
+			git checkout -B $b $t
+			./autogen.sh
+			./configure
+			make
+			make install DESTDIR=$p
+			$checker -l libqb -dump_abi abi_dumps/libqb/$v.xml
+		fi
+	done
+
+	$checker -l libqb \
+	   -d1 abi_dumps/libqb/libqb_$ver1.abi.tar.gz \
+	   -d2 abi_dumps/libqb/libqb_$ver2.abi.tar.gz
+
+	google-chrome compat_reports/libqb/$ver1\_to_$ver2/abi_compat_report.html
+
+	echo mv compat_reports/libqb/$ver1\_to_$ver2/abi_compat_report.html abi_compat_report_$ver1\_to_$ver2.html
+	echo scp abi_compat_report_$ver1\_to_$ver2.html fedorahosted.org:quarterback
+	git checkout master
+}
+
+check_api_sanity() {
+	make
+	export CFLAGS="-Wall -ggdb2"
+	api-sanity-checker -l libqb -d build-aux/api-auto-test.xml -gen -build -run
+	google-chrome test_results/libqb/master/test_results.html
+	google-chrome test_results/libqb/master/test_results.html
+}
+
+check_all() {
+	check_ansi
+	check_nosection
+	check_sysv
+	check_noepoll
+	check_nogettime
+	check_bsd
+	check_dist
+	check_rpm
+}
+
+
+case $command in
+	help)
+	help_all $args
+	;;
+	ansi)
+		check_ansi
+	;;
+	nosection)
+		check_nosection
+	;;
+	sysv)
+		check_sysv
+	;;
+	noepoll)
+		check_noepoll
+	;;
+	nogettime)
+		check_nogettime
+	;;
+	bsd)
+		check_bsd
+	;;
+	mac)
+		check_mac
+	;;
+	rpm)
+		check_rpm
+	;;
+	mock)
+		check_mock
+	;;
+	dist)
+		check_dist
+	;;
+	coverity)
+		check_coverity
+	;;
+	clang)
+		check_clang
+	;;
+	abi)
+		check_abi $args
+	;;
+	api_sanity)
+		check_api_sanity
+	;;
+	all)
+		check_all
+	;;
+	*)
+		help_all
+	;;
+esac
+cd -
+exit 0
+
diff --git a/coding_style.txt b/coding_style.txt
new file mode 100644
index 0000000..a4fc0d0
--- /dev/null
+++ b/coding_style.txt
@@ -0,0 +1,32 @@
+
+		libqb coding style
+
+Our coding style is basically http://www.kernel.org/doc/Documentation/CodingStyle
+
+And any differences will be appended here.
+
+Chapter 5: Typedefs
+- Use int32_t integer types and not "int" / "long".
+
+Chapter 4: Naming
+- functions preferably named object_verb
+
+Chapter 8: Commenting
+- Document public functions using doxygen style comments in the header file.
+  We use doxygen to generate man pages.
+
+
+== Fixing mistakes ==
+Use "./Lindent <file>" to fix any indentation.
+
+== VIM settings ==
+set formatoptions=tcqlron
+set cinoptions=:0,l1,t0,g0
+set cindent 
+set noexpandtab
+set tabstop=8
+set shiftwidth=8
+set textwidth=78
+set smarttab
+
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a4dc7c0
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,620 @@
+
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.61])
+
+AC_INIT([libqb],
+	m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+	[quarterback-devel at fedorahosted.org])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_SRCDIR([lib/ringbuffer.c])
+AC_CONFIG_HEADERS([include/config.h include/qb/qbconfig.h])
+AC_USE_SYSTEM_EXTENSIONS
+
+AM_INIT_AUTOMAKE([-Wno-portability dist-xz])
+dnl automake >= 1.11 offers --enable-silent-rules for suppressing the output from
+dnl normal compilation.  When a failure occurs, it will then display the full
+dnl command line
+dnl Wrap in m4_ifdef to avoid breaking on older platforms
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+LT_PREREQ([2.2.6])
+LT_INIT
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_CANONICAL_HOST
+AC_PROG_LIBTOOL
+AC_LANG([C])
+
+dnl Fix default variables - "prefix" variable if not specified
+if test "$prefix" = "NONE"; then
+	prefix="/usr"
+	if test "$localstatedir" = "\${prefix}/var"; then
+		localstatedir="/var"
+	fi
+	if test "$sysconfdir" = "\${prefix}/etc"; then
+		sysconfdir="/etc"
+	fi
+	if test "$libdir" = "\${exec_prefix}/lib"; then
+		if test -e /usr/lib64; then
+			libdir="/usr/lib64"
+		else
+			libdir="/usr/lib"
+		fi
+	fi
+fi
+
+if test "$srcdir" = "."; then
+	AC_MSG_NOTICE([building in place srcdir:$srcdir])
+	AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place])
+else
+	AC_MSG_NOTICE([building out of tree srcdir:$srcdir])
+fi
+
+# Checks for programs.
+
+# check stolen from gnulib/m4/gnu-make.m4
+if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then
+	AC_MSG_ERROR([you don't seem to have GNU make; it is required])
+fi
+
+AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_CC
+AC_PROG_CPP
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_CHECK_PROGS([PKGCONFIG], [pkg-config])
+AC_CHECK_PROGS([DOXYGEN], [doxygen])
+AM_CONDITIONAL(HAVE_DOXYGEN, test -n "${DOXYGEN}")
+AC_CHECK_PROGS([SPLINT], [splint])
+AM_CONDITIONAL(HAVE_SPLINT, test -n "${SPLINT}")
+
+## local helper functions
+
+# this function checks if CC support options passed as
+# args. Global CFLAGS are ignored during this test.
+cc_supports_flag() {
+	BACKUP="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $@"
+	AC_MSG_CHECKING([whether $CC supports "$@"])
+	AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
+			  [RC=0; AC_MSG_RESULT([yes])],
+			  [RC=1; AC_MSG_RESULT([no])])
+	CPPFLAGS="$BACKUP"
+	return $RC
+}
+
+## cleanup
+AC_MSG_NOTICE(Sanitizing prefix: ${prefix})
+case $prefix in
+  NONE) prefix=/usr/local;;
+esac
+
+AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix})
+case $exec_prefix in
+  NONE)   exec_prefix=$prefix;;
+  prefix) exec_prefix=$prefix;;
+esac
+
+# Checks for libraries.
+dnl librt from glibc NEEDs libpthread
+dnl so. if test for libpthread after librt
+dnl it will always be "none needed", but it is not true
+dnl when linking libraries. Looks like a bug.
+AC_SEARCH_LIBS([pthread_create], [pthread])
+AC_SEARCH_LIBS([mq_open], [rt])
+AC_SEARCH_LIBS([dlopen], [dl])
+AC_SEARCH_LIBS([socket], [socket])
+AC_SEARCH_LIBS([gethostbyname], [nsl])
+
+# look for testing harness "check"
+PKG_CHECK_MODULES([CHECK], [check >= 0.9.4],[with_check=yes],[with_check=no])
+AM_CONDITIONAL(HAVE_CHECK, test "${with_check}" = "yes")
+
+# look for GLIB (used for testing integration)
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.0, have_glib=yes, have_glib=no)
+AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+if test x"$have_glib" = xyes; then
+AC_DEFINE_UNQUOTED([HAVE_GLIB], [1], [We have glib])
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+
+AC_CHECK_HEADERS([arpa/inet.h link.h fcntl.h inttypes.h limits.h netinet/in.h \
+		  stdint.h stddef.h stdlib.h string.h strings.h \
+		  dlfcn.h time.h sys/time.h sys/types.h sys/stat.h \
+		  sys/param.h sys/socket.h sys/time.h sys/poll.h sys/epoll.h \
+		  sys/uio.h sys/event.h sys/sockio.h sys/un.h sys/resource.h \
+		  syslog.h errno.h unistd.h sys/mman.h \
+		  sys/sem.h sys/ipc.h sys/msg.h netdb.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_UID_T
+AC_C_INLINE
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_INT8_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT8_T
+
+AC_CHECK_MEMBER([struct sockaddr_un.sun_len],
+                [AC_DEFINE([HAVE_STRUCT_SOCKADDR_UN_SUN_LEN],
+                           1,
+                           [Define to 1 if struct sockaddr_un has a member sun_len])],
+                [],
+                [#include <sys/un.h>])
+
+AC_MSG_CHECKING(looking for union semun in sys/sem.h)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[#include <sys/types.h>
+  #include <sys/ipc.h>
+  #include <sys/sem.h>]],
+[[union semun arg; semctl(0, 0, 0, arg);]])],
+                    [
+                      AC_MSG_RESULT([yes])
+                      AC_DEFINE_UNQUOTED([HAVE_SEMUN], 1, [Define to 1 if you have union semun.])
+                    ],
+                    [
+                      AC_MSG_RESULT([no])
+                    ]
+                 )
+
+
+AC_MSG_CHECKING(for MSG_NOSIGNAL)
+AC_TRY_COMPILE([#include <sys/socket.h>],
+	       [ int f = MSG_NOSIGNAL; ],
+	       [ AC_MSG_RESULT(yes)
+	         AC_DEFINE(HAVE_MSG_NOSIGNAL, 1,
+	         [Define this symbol if you have MSG_NOSIGNAL])],
+	       [ AC_MSG_RESULT(no)])
+
+AC_MSG_CHECKING(for SO_NOSIGPIPE )
+AC_TRY_COMPILE([#include <sys/socket.h>],
+		[ int f = SO_NOSIGPIPE; ],
+		[ AC_MSG_RESULT(yes)
+		  AC_DEFINE(HAVE_SO_NOSIGPIPE, 1,
+		  [Define this symbol if you have SO_NOSIGPIPE]) ],
+		[ AC_MSG_RESULT(no)])
+
+# Checks for library functions.
+AC_FUNC_CHOWN
+AC_FUNC_FORK
+AC_FUNC_MMAP
+AC_FUNC_STRERROR_R
+AC_CHECK_FUNCS([alarm clock_gettime ftruncate gettimeofday \
+		localtime localtime_r memset munmap socket \
+		strchr strrchr strdup strstr strcasecmp \
+		poll epoll_create epoll_create1 kqueue \
+		random rand getrlimit sysconf \
+		pthread_spin_lock pthread_setschedparam \
+                pthread_mutexattr_setpshared \
+                pthread_condattr_setpshared \
+		sem_timedwait semtimedop \
+		sched_get_priority_max sched_setscheduler \
+		getpeerucred getpeereid])
+
+AM_CONDITIONAL(HAVE_SEM_TIMEDWAIT,
+	       [test "x$ac_cv_func_sem_timedwait" = xyes])
+AM_CONDITIONAL(HAVE_EPOLL,
+	       [test "x$ac_cv_func_epoll_create" = xyes])
+AM_CONDITIONAL(HAVE_POLL,
+	       [test "x$ac_cv_func_poll" = xyes])
+AM_CONDITIONAL(HAVE_KQUEUE,
+	       [test "x$ac_cv_func_kqueue" = xyes])
+
+AC_CONFIG_LIBOBJ_DIR(lib)
+AC_REPLACE_FUNCS(strlcpy strlcat strchrnul)
+
+## local defines
+PACKAGE_FEATURES=""
+
+if test x$ac_cv_func_epoll_create = xyes; then
+	PACKAGE_FEATURES="$PACKAGE_FEATURES epoll"
+fi
+
+nongcc_memory_barrier_needed=no
+arch_force_shmlba=no
+AC_MSG_CHECKING([for architecture in ${host_cpu}])
+case $host_cpu in
+	sparc*)
+		AC_MSG_RESULT([sparc])
+		AC_DEFINE_UNQUOTED([QB_ARCH_SPARC], [1], [sparc])
+		nongcc_memory_barrier_needed=yes
+		arch_force_shmlba=yes
+		;;
+	alpha*)
+		AC_MSG_RESULT([alpha])
+		AC_DEFINE_UNQUOTED([QB_ARCH_ALPHA], [1], [alpha])
+		nongcc_memory_barrier_needed=yes
+		;;
+	powerpc*)
+		AC_MSG_RESULT([powerpc])
+		AC_DEFINE_UNQUOTED([QB_ARCH_POWERPC], [1], [powerpc])
+		ac_cv_link_attribute_section=no
+		nongcc_memory_barrier_needed=yes
+		arch_force_shmlba=yes
+		;;
+	ia64)
+		AC_MSG_RESULT([ia64])
+		AC_DEFINE_UNQUOTED([QB_ARCH_IA64], [1], [ia64])
+		nongcc_memory_barrier_needed=yes
+		;;
+	arm*)
+		AC_MSG_RESULT([ia64])
+		AC_DEFINE_UNQUOTED([QB_ARCH_ARM], [1], [arm])
+		arch_force_shmlba=yes
+		;;
+	mips*)
+		AC_MSG_RESULT([ia64])
+		AC_DEFINE_UNQUOTED([QB_ARCH_MIPS], [1], [mips])
+		arch_force_shmlba=yes
+		;;
+	*)
+		AC_MSG_RESULT([${host_cpu}])
+		;;
+esac
+
+if test $arch_force_shmlba = yes; then
+	AC_DEFINE_UNQUOTED([QB_FORCE_SHM_ALIGN], [1],
+			   [shared and fixed mmap must align on 16k])
+fi
+
+# OS detection
+# THIS SECTION MUST DIE!
+CP=cp
+AC_MSG_CHECKING([for os in ${host_os}])
+case "$host_os" in
+	*linux*)
+		AC_DEFINE_UNQUOTED([QB_LINUX], [1],
+				   [Compiling for Linux platform])
+		AC_MSG_RESULT([Linux])
+	;;
+	*cygwin*)
+		AC_DEFINE_UNQUOTED([QB_CYGWIN], [1],
+				   [Compiling for Cygwin platform])
+		ac_cv_link_attribute_section=no
+		nongcc_memory_barrier_needed=yes
+		gcc_has_builtin_sync_operations=no
+		AC_MSG_RESULT([Cygwin])
+	;;
+	darwin*)
+		AC_DEFINE_UNQUOTED([QB_DARWIN], [1],
+				   [Compiling for Darwin platform])
+		CP=rsync
+		ac_cv_link_attribute_section=no
+		dnl Attribute section appears to work here but fails later with:
+		dnl cc1: error in backend: Global variable 'descriptor.4902'
+		dnl has an invalid section specifier '__verbose': mach-o
+		dnl section specifier requires a segment and section
+		dnl separated by a comma
+		AC_DEFINE_UNQUOTED([DISABLE_POSIX_THREAD_PROCESS_SHARED], [1],
+				   [Disable _POSIX_THREAD_PROCESS_SHARED])
+		AC_MSG_RESULT([Darwin])
+	;;
+	*bsd*)
+		AC_DEFINE_UNQUOTED([QB_BSD], [1],
+				   [Compiling for BSD platform])
+		case "$host_os" in
+			*freebsd[[234567]]*)
+			;;
+			*freebsd*)
+				AC_DEFINE_UNQUOTED([QB_FREEBSD_GE_8], [1],
+					   [Compiling for FreeBSD >= 8 platform])
+			;;
+			*netbsd*)
+				# this is because dlopen within a dl_iterate_phdr
+				# callback locks up.
+				ac_cv_link_attribute_section=no
+				AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [103],
+					   [Unix path length])
+			;;
+			*openbsd*)
+				AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [104],
+					   [Unix path length])
+			;;
+		esac
+		AC_MSG_RESULT([BSD])
+	;;
+	*solaris*)
+		ac_cv_link_attribute_section=no
+		AC_DEFINE_UNQUOTED(DISABLE_IPC_SHM, 1,
+				   [Disable shared mem ipc])
+		AC_DEFINE_UNQUOTED([QB_SOLARIS], [1],
+				   [Compiling for Solaris platform])
+		CP=rsync
+		AC_MSG_RESULT([Solaris])
+	;;
+	*)
+		AC_MSG_ERROR([Unsupported OS? hmmmm])
+	;;
+esac
+
+AC_MSG_CHECKING([whether GCC supports builtin sync intrinsics])
+if test -z "$gcc_has_builtin_sync_operations"; then
+	gcc_has_builtin_sync_operations=no
+	if test x"$GCC" = xyes && test x$have_mingw != xyes; then
+		AC_TRY_LINK([],
+			    [int i;
+			     __sync_synchronize ();
+			     __sync_bool_compare_and_swap (&i, 0, 1);
+			     __sync_fetch_and_add (&i, 1);
+			     ],
+			     [gcc_has_builtin_sync_operations=yes],
+			     [gcc_has_builtin_sync_operations=no])
+	fi
+fi
+AC_MSG_RESULT($gcc_has_builtin_sync_operations)
+AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS,
+	       [test "x$gcc_has_builtin_sync_operations" = xyes])
+
+if test "x$gcc_has_builtin_sync_operations" = xyes; then
+	AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS, 1,
+			   [have builtin sync operations])
+fi
+
+# __atomic_XXX
+AC_MSG_CHECKING([whether GCC supports builtin atomic intrinsics])
+if test -z "$gcc_has_builtin_atomic_operations"; then
+	gcc_has_builtin_atomic_operations=no
+	if test x"$GCC" = xyes && test x$have_mingw != xyes; then
+		AC_TRY_LINK([],
+			    [int i;
+			     __atomic_load_n(&i, __ATOMIC_ACQUIRE);
+			     __atomic_exchange_n(&i, 0, __ATOMIC_RELEASE);
+			     ],
+			     [gcc_has_builtin_atomic_operations=yes],
+			     [gcc_has_builtin_atomic_operations=no])
+	fi
+fi
+AC_MSG_RESULT($gcc_has_builtin_atomic_operations)
+AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS,
+	       [test "x$gcc_has_builtin_atomic_operations" = xyes])
+
+if test "x$gcc_has_builtin_atomic_operations" = xyes; then
+	AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS, 1,
+			   [have builtin atomic operations])
+fi
+
+
+AC_MSG_CHECKING([whether atomics need memory barrier])
+if test -n "$ac_cv_atomic_need_memory_barrier"; then
+	memory_barrier_needed=$ac_cv_atomic_need_memory_barrier
+else
+	if test x$gcc_has_builtin_sync_operations = xyes; then
+		memory_barrier_needed=yes
+		PACKAGE_FEATURES="$PACKAGE_FEATURES gcc__sync"
+	else
+		memory_barrier_needed=$nongcc_memory_barrier_needed
+		AC_MSG_WARN([-----------------------------])
+		AC_MSG_WARN([You have gcc but not __sync_bool_compare_and_swap])
+		AC_MSG_WARN([try CFLAGS="-march=<your arch> -mtune=native" ./configure])
+		AC_MSG_WARN([-----------------------------])
+	fi
+fi
+AC_MSG_RESULT($memory_barrier_needed)
+
+if test x"$memory_barrier_needed" != xno; then
+	AC_DEFINE_UNQUOTED(QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED, 1,
+			   [need atomic memory barrier])
+fi
+
+LINT_FLAGS="-syntax -weak -unrecog +posixlib +ignoresigns -fcnuse \
+	-badflag -D__gnuc_va_list=va_list -D__attribute\(x\)= \
+	-warnposix +matchanyintegral -sysunrecog"
+
+# local options
+AC_ARG_ENABLE([ansi],
+	[  --enable-ansi           : force to build with ANSI standards. ],
+	[ default="no" ])
+
+AC_ARG_ENABLE([fatal-warnings],
+	[  --enable-fatal-warnings : enable fatal warnings. ],
+	[ default="no" ])
+
+AC_ARG_ENABLE([debug],
+	[  --enable-debug          : enable debug build. ],
+	[ default="no" ])
+
+AC_ARG_ENABLE([coverage],
+	[  --enable-coverage       : coverage analysis of the codebase. ],
+	[ default="no" ])
+
+AC_ARG_ENABLE([slow-tests],
+	[  --enable-slow-tests     : build and run slow tests. ],
+	[ default="no" ])
+
+AC_ARG_WITH([socket-dir],
+	[  --with-socket-dir=DIR   : socket dir. ],
+	[ SOCKETDIR="$withval" ],
+	[ SOCKETDIR="$localstatedir/run" ])
+
+AC_SUBST(CP)
+# *FLAGS handling goes here
+
+ENV_CFLAGS="$CFLAGS"
+ENV_CPPFLAGS="$CPPFLAGS"
+ENV_LDFLAGS="$LDFLAGS"
+
+# debug build stuff
+if test "x${enable_debug}" = xyes; then
+	AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code])
+	OPT_CFLAGS="-O0"
+	if test "x${GCC}" = xyes; then
+		GDB_FLAGS="-ggdb3"
+	else
+		GDB_FLAGS="-g"
+	fi
+	PACKAGE_FEATURES="$PACKAGE_FEATURES debug"
+fi
+
+# extra warnings
+EXTRA_WARNINGS=""
+
+WARNLIST="
+	all
+	shadow
+	missing-prototypes
+	missing-declarations
+	strict-prototypes
+	declaration-after-statement
+	pointer-arith
+	write-strings
+	cast-align
+	bad-function-cast
+	missing-format-attribute
+	format=2
+	format-security
+	no-format-nonliteral
+	no-long-long
+	unsigned-char
+	gnu89-inline
+	no-strict-aliasing
+	"
+
+for j in $WARNLIST; do
+	if cc_supports_flag -W$j; then
+		EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";
+	fi
+done
+
+# --- coverage ---
+if test "x${enable_coverage}" = xyes && \
+		cc_supports_flag -ftest-coverage && \
+		cc_supports_flag -fprofile-arcs ; then
+	AC_MSG_NOTICE([Enabling Coverage (enable -O0 by default)])
+	OPT_CFLAGS="-O0"
+	COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs"
+	COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs"
+	PACKAGE_FEATURES="$PACKAGE_FEATURES coverage"
+else
+	COVERAGE_CFLAGS=""
+	COVERAGE_LDFLAGS=""
+fi
+
+if test "x${enable_slow_tests}" = xyes ; then
+	AC_DEFINE([HAVE_SLOW_TESTS], 1,[have slow tests])
+	AC_MSG_NOTICE([Enabling Slow tests])
+fi
+AM_CONDITIONAL(HAVE_SLOW_TESTS, [test "x${enable_slow_tests}" = xyes])
+AC_SUBST(HAVE_SLOW_TESTS)
+
+# --- callsite sections ---
+if test "x${GCC}" = xyes; then
+	AC_MSG_CHECKING([whether GCC supports __attribute__((section())])
+	if test "x${ac_cv_link_attribute_section}" = x ; then
+		AC_TRY_LINK([],
+			    [static int my_var __attribute__((section("__verbose"))) = 5;
+			     if (my_var == 5) return 0;
+			     else return -1;
+			     ],
+			     [gcc_has_attribute_section=yes],
+			     [gcc_has_attribute_section=no])
+	else
+		gcc_has_attribute_section=${ac_cv_link_attribute_section}
+	fi
+
+	AC_MSG_RESULT($gcc_has_attribute_section)
+	if test $gcc_has_attribute_section = yes; then
+		AC_DEFINE([QB_HAVE_ATTRIBUTE_SECTION], 1,
+			  [Enabling code using __attribute__((section))])
+		PACKAGE_FEATURES="$PACKAGE_FEATURES attribute-section"
+	fi
+fi
+
+# --- ansi ---
+if test "x${enable_ansi}" = xyes && \
+		cc_supports_flag -std=iso9899:199409 ; then
+	AC_MSG_NOTICE([Enabling ANSI Compatibility])
+	ANSI_CPPFLAGS="-ansi -D_GNU_SOURCE -DANSI_ONLY"
+	PACKAGE_FEATURES="$PACKAGE_FEATURES ansi"
+else
+	ANSI_CPPFLAGS=""
+fi
+
+# --- fatal warnings ---
+if test "x${enable_fatal_warnings}" = xyes && \
+		cc_supports_flag -Werror ; then
+	AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)])
+	WERROR_CFLAGS="-Werror"
+	PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings"
+else
+	WERROR_CFLAGS=""
+fi
+
+# final build of *FLAGS
+CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \
+	$COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS"
+CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS"
+LDFLAGS="$ENV_LDFLAGS $COVERAGE_LDFLAGS"
+
+if test -f /usr/share/dict/words ; then
+	HAVE_DICT_WORDS=yes
+	AC_DEFINE([HAVE_DICT_WORDS], 1, "Have /usr/share/dict/words")
+fi
+AM_CONDITIONAL([HAVE_DICT_WORDS], [test "x$HAVE_DICT_WORDS" = xyes])
+
+# substitute what we need:
+AC_SUBST([SOCKETDIR])
+AC_SUBST([LINT_FLAGS])
+
+AC_DEFINE_UNQUOTED([SOCKETDIR], "$(eval echo ${SOCKETDIR})", [Socket directory])
+AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory])
+AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [quarterback built-in features])
+
+AC_CONFIG_FILES([Makefile
+		 include/Makefile
+		 include/qb/Makefile
+		 lib/Makefile
+		 lib/libqb.pc
+		 tools/Makefile
+		 tests/Makefile
+		 tests/test.conf
+		 examples/Makefile
+		 docs/Makefile
+		 docs/man.dox
+		 docs/html.dox])
+
+AC_OUTPUT
+
+AC_MSG_RESULT([])
+AC_MSG_RESULT([$PACKAGE configuration:])
+AC_MSG_RESULT([  Version                  = ${VERSION}])
+AC_MSG_RESULT([  Prefix                   = ${prefix}])
+AC_MSG_RESULT([  Executables              = ${sbindir}])
+AC_MSG_RESULT([  Man pages                = ${mandir}])
+AC_MSG_RESULT([  Doc dir                  = ${docdir}])
+AC_MSG_RESULT([  Libraries                = ${libdir}])
+AC_MSG_RESULT([  Header files             = ${includedir}])
+AC_MSG_RESULT([  Arch-independent files   = ${datadir}])
+AC_MSG_RESULT([  State information        = ${localstatedir}])
+AC_MSG_RESULT([  System configuration     = ${sysconfdir}])
+AC_MSG_RESULT([  SOCKETDIR                = ${SOCKETDIR}])
+AC_MSG_RESULT([  Features                 =${PACKAGE_FEATURES}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([$PACKAGE build info:])
+AC_MSG_RESULT([  Optimization             = ${OPT_CFLAGS}])
+AC_MSG_RESULT([  Debug options            = ${GDB_CFLAGS}])
+AC_MSG_RESULT([  Extra compiler warnings  = ${EXTRA_WARNING}])
+AC_MSG_RESULT([  Env. defined CFLAG       = ${ENV_CFLAGS}])
+AC_MSG_RESULT([  Env. defined CPPFLAGS    = ${ENV_CPPFLAGS}])
+AC_MSG_RESULT([  Env. defined LDFLAGS     = ${ENV_LDFLAGS}])
+AC_MSG_RESULT([  ANSI defined CPPFLAGS    = ${ANSI_CPPFLAGS}])
+AC_MSG_RESULT([  Coverage     CFLAGS      = ${COVERAGE_CFLAGS}])
+AC_MSG_RESULT([  Coverage     LDFLAGS     = ${COVERAGE_LDFLAGS}])
+AC_MSG_RESULT([  Fatal War.   CFLAGS      = ${WERROR_CFLAGS}])
+AC_MSG_RESULT([  Final        CFLAGS      = ${CFLAGS}])
+AC_MSG_RESULT([  Final        CPPFLAGS    = ${CPPFLAGS}])
+AC_MSG_RESULT([  Final        LDFLAGS     = ${LDFLAGS}])
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..06dd00e
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,3 @@
+man3
+html
+*.dox
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 0000000..f4b6197
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1,48 @@
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+
+MAINTAINERCLEANFILES	= Makefile.in
+EXTRA_DIST 		= man.dox html.dox
+noinst_HEADERS          = mainpage.h
+
+dist_man_MANS = man8/qb-blackbox.8
+if HAVE_DOXYGEN
+inc_dir = $(top_srcdir)/include/qb
+dependant_headers = $(wildcard $(inc_dir)/qb*.h)
+dist_man_MANS += man3/qbipcc.h.3 man3/qbipcs.h.3 man3/qbatomic.h.3 \
+		 man3/qbhdb.h.3 man3/qbipc_common.h.3 man3/qblist.h.3 \
+		 man3/qbloop.h.3 man3/qbutil.h.3 man3/qbarray.h.3 \
+		 man3/qblog.h.3 man3/qbmap.h.3
+
+
+$(dist_man_MANS): man.dox $(dependant_headers)
+	@mkdir -p man3
+	@doxygen man.dox
+
+doxygen: html.dox
+	@mkdir -p html
+	@doxygen html.dox
+
+else
+doxygen:
+	@echo WARNING: no doxygen to build man pages!
+endif
+
+clean-generic:
+	rm -rf html man3
diff --git a/docs/html.dox.in b/docs/html.dox.in
new file mode 100644
index 0000000..abeef17
--- /dev/null
+++ b/docs/html.dox.in
@@ -0,0 +1,213 @@
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = libqb
+PROJECT_NUMBER         = @VERSION@
+OUTPUT_DIRECTORY       = .
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = YES
+ALWAYS_DETAILED_SEC    = YES
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = NO
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      =
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = NO
+DISTRIBUTE_GROUP_DOC   = YES
+SUBGROUPING            = NO
+TYPEDEF_HIDES_STRUCT   = YES
+SYMBOL_CACHE_SIZE      = 0
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = YES
+EXTRACT_ANON_NSPACES   = YES
+HIDE_UNDOC_MEMBERS     = YES
+HIDE_UNDOC_CLASSES     = YES
+HIDE_FRIEND_COMPOUNDS  = YES
+HIDE_IN_BODY_DOCS      = YES
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            =
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+INPUT                  = @srcdir@/../include/qb/ @srcdir@/../docs/
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.h
+RECURSIVE              = NO
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           = @srcdir@/../examples
+EXAMPLE_PATTERNS       = *.c
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_TIMESTAMP         = NO
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+HTML_DYNAMIC_SECTIONS  = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = YES
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          =
+QHP_VIRTUAL_FOLDER     = doc
+QHP_CUST_FILTER_NAME   =
+QHP_CUST_FILTER_ATTRS  =
+QHP_SECT_FILTER_ATTRS  =
+QHG_LOCATION           =
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+USE_INLINE_TREES       = NO
+TREEVIEW_WIDTH         = 250
+FORMULA_FONTSIZE       = 10
+SEARCHENGINE           = YES
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+LATEX_SOURCE_CODE      = NO
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = .
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+GENERATE_RTF           = NO
+GENERATE_XML           = NO
+GENERATE_AUTOGEN_DEF   = NO
+GENERATE_PERLMOD       = NO
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = @srcdir@/../include
+INCLUDE_FILE_PATTERNS  = *.h
+PREDEFINED             = 
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+#
+#
+TAGFILES               =
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = NO
+PERL_PATH              =
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+DOT_FONTNAME           = FreeSans
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = YES
+TEMPLATE_RELATIONS     = YES
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/docs/mainpage.h b/docs/mainpage.h
new file mode 100644
index 0000000..6a6761d
--- /dev/null
+++ b/docs/mainpage.h
@@ -0,0 +1,117 @@
+/**
+ * @mainpage
+ *
+ * @section overview Overview
+ *
+ * libqb is a thread-safe library with the primary purpose of providing high
+ * performance client server reusable features.
+ *
+ * It provides high performance ipc, and poll.
+ *
+ * We don't intend be an all encompassing library, but instead provide very
+ * specially focused APIs that are highly
+ *
+ * tuned for maximum performance for client/server applications.
+ *
+ * See the following pages for more info:
+ * - @subpage qb_list_overview
+ * - @subpage qb_atomic_overview
+ * - @subpage qb_array_overview
+ * - @subpage qb_map_overview
+ * - @subpage qb_hdb_overview
+ * - @subpage qb_rb_overview
+ * - @subpage qb_loop_overview
+ * - @subpage qb_log_overview
+ * - @subpage qb_ipc_overview
+ * - @subpage qb_util_overview
+ */
+
+/**
+ * @page qb_rb_overview Ringbuffer
+ * @copydoc qbrb.h
+ * @see qbrb.h
+ */
+
+/**
+ * @page qb_list_overview List
+ * @copydoc qblist.h
+ * @see qblist.h
+ */
+
+/**
+ * @page qb_array_overview Array
+ * @copydoc qbarray.h
+ * @see qbarray.h
+ */
+
+/**
+ * @page qb_map_overview Map
+ * @copydoc qbmap.h
+ * @see qbmap.h
+ */
+
+/** 
+ * @page qb_hdb_overview Handle Database
+ * @copydoc qbhdb.h
+ * @see qbhdb.h
+ */
+
+/**
+ * @page qb_loop_overview Main Loop
+ * @copydoc qbloop.h
+ * @see qbloop.h
+ */
+
+/**
+ * @page qb_log_overview Logging
+ * @copydoc qblog.h
+ * @see qblog.h
+ */
+
+/**
+ * @page qb_ipc_overview IPC Overview
+ *
+ * @par Overview
+ * libqb provides a generically reusable very high performance shared memory IPC sytem for client
+ * and service applications.  It supports many features including:
+ * - Multiple transport implementations
+ *   -# Shared memory implementation for very high performance.
+ *   -# Unix sockets
+ * - A synchronous request/response channel and asynchronous response channel per ipc connection.
+ * - User defined private data per IPC connection.
+ * - Ability to call a function per service on ipc connection and disconnection.
+ * - Authenticated IPC connection with ability for developer to define which UIDs and GIDs are valid at connection time.
+ * - Fully abstracted poll system so that any poll library may be used.
+ * - User defined selector for determining the proper function to call per service and id.
+ *
+ * @par Security
+ * The ipc system uses default operating system security mechanics to ensure ipc 
+ * connections are validated.  A callback used with qb_ipcs_create() is called
+ * for every new ipc connection with the parameters of UID and GID.  The callback
+ * then determines if the UID and GID are authenticated for communication.
+ *
+ * @par Performance
+ * For performance QB_IPC_SHM (shared memory) is recogmended. It is tuned for
+ * very high performance.
+ *
+ * @par Client API
+ * @copydoc qbipcc.h
+ * @see qbipcc.h
+ *
+ * @par Server API
+ * @copydoc qbipcs.h
+ * @see qbipcs.h
+ *
+ */
+
+/**
+ * @page qb_atomic_overview Atomic operations
+ * @copydoc qbatomic.h
+ * @see qbatomic.h
+ */
+
+/**
+ * @page qb_util_overview Common Utilities
+ * @copydoc qbutil.h
+ * @see qbutil.h
+ */
diff --git a/docs/man.dox.in b/docs/man.dox.in
new file mode 100644
index 0000000..f88e216
--- /dev/null
+++ b/docs/man.dox.in
@@ -0,0 +1,161 @@
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = libqb
+PROJECT_NUMBER         = @VERSION@
+OUTPUT_DIRECTORY       = .
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = YES
+ALWAYS_DETAILED_SEC    = YES
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = NO
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      =
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = NO
+DISTRIBUTE_GROUP_DOC   = YES
+SUBGROUPING            = NO
+TYPEDEF_HIDES_STRUCT   = YES
+SYMBOL_CACHE_SIZE      = 0
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = YES
+EXTRACT_ANON_NSPACES   = YES
+HIDE_UNDOC_MEMBERS     = YES
+HIDE_UNDOC_CLASSES     = YES
+HIDE_FRIEND_COMPOUNDS  = YES
+HIDE_IN_BODY_DOCS      = YES
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = NO
+GENERATE_TESTLIST      = NO
+GENERATE_BUGLIST       = NO
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            =
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+INPUT                  = @srcdir@/../include/qb/ @srcdir@/../docs/
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.h
+RECURSIVE              = YES
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+GENERATE_MAN           = YES
+MAN_OUTPUT             = .
+MAN_EXTENSION          = .3
+MAN_LINKS              = YES
+GENERATE_HTML          = NO
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+GENERATE_RTF           = NO
+GENERATE_XML           = NO
+GENERATE_AUTOGEN_DEF   = NO
+GENERATE_PERLMOD       = NO
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = YES
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = ../include
+INCLUDE_FILE_PATTERNS  = *.h
+PREDEFINED             = 
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = NO
+#---------------------------------------------------------------------------
+#
+#
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = NO
+PERL_PATH              =
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = NO
+MSCGEN_PATH            =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+DOT_FONTNAME           = FreeSans
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = NO
+COLLABORATION_GRAPH    = NO
+GROUP_GRAPHS           = NO
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = NO
+INCLUDED_BY_GRAPH      = NO
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = NO
+DIRECTORY_GRAPH        = NO
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/docs/man8/qb-blackbox.8 b/docs/man8/qb-blackbox.8
new file mode 100644
index 0000000..976848c
--- /dev/null
+++ b/docs/man8/qb-blackbox.8
@@ -0,0 +1,59 @@
+.\"/*
+.\" * Copyright (C) 2012 Red Hat, Inc.
+.\" *
+.\" * Author: Angus Salkeld <asalkeld at redhat.com>
+.\" *
+.\" * This file is part of libqb.
+.\" *
+.\" * libqb is free software: you can redistribute it and/or modify
+.\" * it under the terms of the GNU Lesser General Public License as published by
+.\" * the Free Software Foundation, either version 2.1 of the License, or
+.\" * (at your option) any later version.
+.\" *
+.\" * libqb 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 Lesser General Public License for more details.
+.\" *
+.\" * You should have received a copy of the GNU Lesser General Public License
+.\" * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+.\" */
+.TH QB-BLACKBOX 8 2012-05-28
+.SH NAME
+qb-blackbox \- Display "flight data" from the "blackbox".
+.SH SYNOPSIS
+.B "qb-blackbox <blackbox files>..."
+.SH DESCRIPTION
+.B qb-blackbox
+Print out the logs (including debug logs) that were recorded using libqb's blackbox.
+.SH EXAMPLES
+.TP
+Get info to help developers diagnose a crash.
+.br
+$ qb-blackbox /var/lib/corosync/fdata
+.br
+Dumping the contents of /var/lib/corosync/fdata
+.br
+Mar 23 20:58:57 message_handler_orf_token():3677 install seq 0 aru 0 high seq received 0
+.br
+Mar 23 20:58:57 message_handler_orf_token():3696 retrans flag count 4 token aru 0 install seq 0 aru 0 0
+.br
+Mar 23 20:58:57 old_ring_state_reset():1487 Resetting old ring state
+.br
+Mar 23 20:58:57 deliver_messages_from_recovery_to_regular():1693 recovery to regular 1-0
+.br
+Mar 23 20:58:57 memb_state_operational_enter():1779 Delivering to app 1 to 0
+.br
+Mar 23 20:58:57 sync_abort():594 ENTERING sync_abort()
+.br
+Mar 23 20:58:57 sync_save_transitional():586 ENTERING sync_save_transitional()
+.br
+Mar 23 20:58:57 member_object_joined():301 Member joined: r(0) ip(192.168.122.1) 
+.br
+Mar 23 20:58:57 sync_abort():594 ENTERING sync_abort()
+.br
+.SH SEE ALSO
+.BR qblog.h (3),
+.SH AUTHOR
+Angus Salkeld
+.PP
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..40990d8
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,6 @@
+simplelog
+tcpclient
+tcpserver
+ipcclient
+ipcserver
+mapnotify
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..d546fca
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,51 @@
+# Copyright (c) 2011 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+#
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST =
+CLEANFILES =
+
+noinst_PROGRAMS = mapnotify simplelog tcpclient tcpserver ipcclient ipcserver
+
+mapnotify_SOURCES = mapnotify.c $(top_builddir)/include/qb/qbmap.h
+mapnotify_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+mapnotify_LDADD = $(top_builddir)/lib/libqb.la
+
+simplelog_SOURCES = simplelog.c $(top_builddir)/include/qb/qblog.h
+simplelog_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+simplelog_LDADD = $(top_builddir)/lib/libqb.la
+
+tcpclient_SOURCES = tcpclient.c $(top_builddir)/include/qb/qbloop.h
+tcpclient_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+tcpclient_LDADD = $(top_builddir)/lib/libqb.la
+
+tcpserver_SOURCES = tcpserver.c $(top_builddir)/include/qb/qbloop.h
+tcpserver_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+tcpserver_LDADD = $(top_builddir)/lib/libqb.la
+
+ipcclient_SOURCES = ipcclient.c $(top_builddir)/include/qb/qbloop.h \
+		    $(top_builddir)/include/qb/qbipcc.h
+ipcclient_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+ipcclient_LDADD = $(top_builddir)/lib/libqb.la
+
+ipcserver_SOURCES = ipcserver.c $(top_builddir)/include/qb/qbloop.h \
+		    $(top_builddir)/include/qb/qbipcs.h
+ipcserver_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include \
+		     $(GLIB_CFLAGS)
+ipcserver_LDADD = $(top_builddir)/lib/libqb.la $(GLIB_LIBS)
diff --git a/examples/ipcclient.c b/examples/ipcclient.c
new file mode 100644
index 0000000..9f85a9a
--- /dev/null
+++ b/examples/ipcclient.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qbipcc.h>
+#include <qb/qblog.h>
+
+static int32_t do_benchmark = QB_FALSE;
+static int32_t use_events = QB_FALSE;
+static int alarm_notice;
+static qb_util_stopwatch_t *sw;
+#define ONE_MEG 1048576
+static char *data;
+
+struct my_req {
+	struct qb_ipc_request_header hdr;
+	char message[256];
+};
+
+struct my_res {
+	struct qb_ipc_response_header hdr;
+	char message[256];
+};
+
+static void sigalrm_handler (int num)
+{
+	alarm_notice = 1;
+}
+
+static void
+_benchmark(qb_ipcc_connection_t *conn, int write_size)
+{
+	struct iovec iov[2];
+	ssize_t res;
+	struct qb_ipc_request_header hdr;
+	int write_count = 0;
+	float secs;
+
+	alarm_notice = 0;
+	hdr.size = write_size;
+	hdr.id = QB_IPC_MSG_USER_START + 1;
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(struct qb_ipc_request_header);
+
+	iov[1].iov_base = data;
+	iov[1].iov_len = write_size - sizeof(struct qb_ipc_request_header);
+
+	alarm (10);
+
+	qb_util_stopwatch_start(sw);
+	do {
+		res = qb_ipcc_sendv(conn, iov, 2);
+		if (res == write_size) {
+			write_count++;
+		}
+	} while (alarm_notice == 0 && (res == write_size || res == -EAGAIN));
+	if (res < 0) {
+		perror("qb_ipcc_sendv");
+	}
+	qb_util_stopwatch_stop(sw);
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+
+	printf ("%5d messages sent ", write_count);
+	printf ("%5d bytes per write ", write_size);
+	printf ("%7.3f Seconds runtime ", secs);
+	printf ("%9.3f TP/s ",
+		((float)write_count) / secs);
+	printf ("%7.3f MB/s.\n",
+		((float)write_count) * ((float)write_size) / secs);
+}
+
+
+static void
+do_throughput_benchmark(qb_ipcc_connection_t *conn)
+{
+	ssize_t size = 64;
+	int i;
+
+	signal (SIGALRM, sigalrm_handler);
+	sw =  qb_util_stopwatch_create();
+
+	for (i = 0; i < 10; i++) { /* number of repetitions - up to 50k */
+		_benchmark (conn, size);
+		signal (SIGALRM, sigalrm_handler);
+		size *= 5;
+		if (size >= (ONE_MEG - 100)) {
+			break;
+		}
+	}
+}
+
+static void
+do_echo(qb_ipcc_connection_t *conn)
+{
+	struct my_req req;
+	struct my_res res;
+	char *newline;
+	int32_t rc;
+	int32_t send_ten_events;
+
+	while (1) {
+		printf("SEND (q or Q to quit) : ");
+		if (fgets(req.message, 256, stdin) == NULL) {
+			continue;
+		}
+		newline = strrchr(req.message, '\n');
+		if (newline) {
+			*newline = '\0';
+		}
+
+		if (strcasecmp(req.message, "q") == 0) {
+			break;
+		} else {
+			req.hdr.id = QB_IPC_MSG_USER_START + 3;
+			req.hdr.size = sizeof(struct my_req);
+			rc = qb_ipcc_send(conn, &req, req.hdr.size);
+			if (rc < 0) {
+				perror("qb_ipcc_send");
+				exit(0);
+			}
+		}
+
+		send_ten_events = (strcasecmp(req.message, "events") == 0);
+
+		if (rc > 0) {
+			if (use_events && !send_ten_events) {
+				printf("waiting for event recv\n");
+				rc = qb_ipcc_event_recv(conn, &res, sizeof(res), -1);
+			} else {
+				printf("waiting for recv\n");
+				rc = qb_ipcc_recv(conn, &res, sizeof(res), -1);
+			}
+			printf("recv %d\n", rc);
+			if (rc < 0) {
+				perror("qb_ipcc_recv");
+				exit(0);
+			}
+			if (send_ten_events) {
+				int32_t i;
+				printf("waiting for 10 events\n");
+				for (i = 0; i < 10; i++) {
+					rc = qb_ipcc_event_recv(conn, &res, sizeof(res), -1);
+					if (rc < 0) {
+						perror("qb_ipcc_event_recv");
+					} else {
+						printf("got event %d rc:%d\n", i, rc);
+					}
+				}
+			}
+			printf("Response[%d]: %s \n", res.hdr.id, res.message);
+		}
+	}
+}
+
+static void
+show_usage(const char *name)
+{
+	printf("usage: \n");
+	printf("%s <options>\n", name);
+	printf("\n");
+	printf("  options:\n");
+	printf("\n");
+	printf("  -h             show this help text\n");
+	printf("  -b             benchmark\n");
+	printf("  -e             use events instead of responses\n");
+	printf("\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+	qb_ipcc_connection_t *conn;
+	const char *options = "ebh";
+	int32_t opt;
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'b':
+			do_benchmark = QB_TRUE;
+			break;
+		case 'e':
+			use_events = QB_TRUE;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+
+
+	qb_log_init("ipcclient", LOG_USER, LOG_TRACE);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_format_set(QB_LOG_STDERR, "%f:%l [%p] %b");
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	/* Our example server is enforcing a buffer size minimum,
+	 * so the client does not need to be concerned with setting
+	 * the buffer size */
+	conn = qb_ipcc_connect("ipcserver", 0);
+	if (conn == NULL) {
+		perror("qb_ipcc_connect");
+		exit(1);
+	}
+	data = calloc(1, qb_ipcc_get_buffer_size(conn));
+
+	if (do_benchmark) {
+		do_throughput_benchmark(conn);
+	} else {
+		do_echo(conn);
+	}
+
+	qb_ipcc_disconnect(conn);
+	free(data);
+	return EXIT_SUCCESS;
+}
diff --git a/examples/ipcserver.c b/examples/ipcserver.c
new file mode 100644
index 0000000..356c038
--- /dev/null
+++ b/examples/ipcserver.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+#include <qb/qbloop.h>
+#include <qb/qbipcs.h>
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+static GMainLoop *glib_loop;
+static qb_array_t *gio_map;
+#endif /* HAVE_GLIB */
+
+#define ONE_MEG 1048576
+
+static int32_t use_glib = QB_FALSE;
+static int32_t use_events = QB_FALSE;
+static qb_loop_t *bms_loop;
+static qb_ipcs_service_t *s1;
+
+static int32_t
+s1_connection_accept_fn(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
+{
+#if 0
+	if (uid == 0 && gid == 0) {
+		qb_log(LOG_INFO, "Authenticated connection");
+		return 1;
+	}
+	qb_log(LOG_NOTICE, "BAD user!");
+	return 0;
+#else
+	return 0;
+#endif
+}
+
+static void
+s1_connection_created_fn(qb_ipcs_connection_t * c)
+{
+	struct qb_ipcs_stats srv_stats;
+
+	qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
+	qb_log(LOG_INFO, "Connection created (active:%d, closed:%d)",
+	       srv_stats.active_connections, srv_stats.closed_connections);
+}
+
+static void
+s1_connection_destroyed_fn(qb_ipcs_connection_t * c)
+{
+	qb_log(LOG_INFO, "Connection about to be freed");
+}
+
+static int32_t
+s1_connection_closed_fn(qb_ipcs_connection_t * c)
+{
+	struct qb_ipcs_connection_stats stats;
+	struct qb_ipcs_stats srv_stats;
+
+	qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
+	qb_ipcs_connection_stats_get(c, &stats, QB_FALSE);
+	qb_log(LOG_INFO,
+	       "Connection to pid:%d destroyed (active:%d, closed:%d)",
+	       stats.client_pid, srv_stats.active_connections,
+	       srv_stats.closed_connections);
+
+	qb_log(LOG_DEBUG, " Requests     %"PRIu64"", stats.requests);
+	qb_log(LOG_DEBUG, " Responses    %"PRIu64"", stats.responses);
+	qb_log(LOG_DEBUG, " Events       %"PRIu64"", stats.events);
+	qb_log(LOG_DEBUG, " Send retries %"PRIu64"", stats.send_retries);
+	qb_log(LOG_DEBUG, " Recv retries %"PRIu64"", stats.recv_retries);
+	qb_log(LOG_DEBUG, " FC state     %d", stats.flow_control_state);
+	qb_log(LOG_DEBUG, " FC count     %"PRIu64"", stats.flow_control_count);
+	return 0;
+}
+
+struct my_req {
+	struct qb_ipc_request_header hdr;
+	char message[256];
+};
+
+static int32_t
+s1_msg_process_fn(qb_ipcs_connection_t * c, void *data, size_t size)
+{
+	struct qb_ipc_request_header *hdr;
+	struct my_req *req_pt;
+	struct qb_ipc_response_header response;
+	ssize_t res;
+	struct iovec iov[2];
+	char resp[100];
+	int32_t sl;
+	int32_t send_ten_events = QB_FALSE;
+
+	hdr = (struct qb_ipc_request_header *)data;
+	if (hdr->id == (QB_IPC_MSG_USER_START + 1)) {
+		return 0;
+	}
+
+	req_pt = (struct my_req *)data;
+	qb_log(LOG_DEBUG, "msg received (id:%d, size:%d, data:%s)",
+	       req_pt->hdr.id, req_pt->hdr.size, req_pt->message);
+
+	if (strcmp(req_pt->message, "kill") == 0) {
+		exit(0);
+	}
+	response.size = sizeof(struct qb_ipc_response_header);
+	response.id = 13;
+	response.error = 0;
+
+	sl = snprintf(resp, 100, "ACK %zd bytes", size) + 1;
+	iov[0].iov_len = sizeof(response);
+	iov[0].iov_base = &response;
+	iov[1].iov_len = sl;
+	iov[1].iov_base = resp;
+	response.size += sl;
+
+	send_ten_events = (strcmp(req_pt->message, "events") == 0);
+
+	if (use_events && !send_ten_events) {
+		res = qb_ipcs_event_sendv(c, iov, 2);
+	} else {
+		res = qb_ipcs_response_sendv(c, iov, 2);
+	}
+	if (res < 0) {
+		errno = - res;
+		qb_perror(LOG_ERR, "qb_ipcs_response_send");
+	}
+	if (send_ten_events) {
+		int32_t i;
+		qb_log(LOG_INFO, "request to send 10 events");
+		for (i = 0; i < 10; i++) {
+			res = qb_ipcs_event_sendv(c, iov, 2);
+			qb_log(LOG_INFO, "sent event %d res:%d", i, res);
+		}
+	}
+	return 0;
+}
+
+static void
+sigusr1_handler(int32_t num)
+{
+	qb_log(LOG_DEBUG, "(%d)", num);
+	qb_ipcs_destroy(s1);
+	exit(0);
+}
+
+static void
+show_usage(const char *name)
+{
+	printf("usage: \n");
+	printf("%s <options>\n", name);
+	printf("\n");
+	printf("  options:\n");
+	printf("\n");
+	printf("  -h             show this help text\n");
+	printf("  -m             use shared memory\n");
+	printf("  -u             use unix sockets\n");
+	printf("  -g             use glib mainloop\n");
+	printf("  -e             use events\n");
+	printf("\n");
+}
+
+#ifdef HAVE_GLIB
+struct gio_to_qb_poll {
+	gboolean is_used;
+	int32_t events;
+	int32_t source;
+	int32_t fd;
+	void *data;
+	qb_ipcs_dispatch_fn_t fn;
+	enum qb_loop_priority p;
+};
+
+static gboolean
+gio_read_socket(GIOChannel * gio, GIOCondition condition, gpointer data)
+{
+	struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
+	gint fd = g_io_channel_unix_get_fd(gio);
+
+	return (adaptor->fn(fd, condition, adaptor->data) == 0);
+}
+
+static void
+gio_poll_destroy(gpointer data)
+{
+	struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
+
+	qb_log(LOG_DEBUG, "fd %d adaptor destroyed\n", adaptor->fd);
+	adaptor->is_used = QB_FALSE;
+	adaptor->fd = 0;
+}
+
+static int32_t
+my_g_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
+		  void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	struct gio_to_qb_poll *adaptor;
+	GIOChannel *channel;
+	int32_t res = 0;
+
+	res = qb_array_index(gio_map, fd, (void **)&adaptor);
+	if (res < 0) {
+		return res;
+	}
+	if (adaptor->is_used) {
+		return -EEXIST;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+	if (!channel) {
+		return -ENOMEM;
+	}
+
+	adaptor->fn = fn;
+	adaptor->events = evts;
+	adaptor->data = data;
+	adaptor->p = p;
+	adaptor->is_used = TRUE;
+	adaptor->fd = fd;
+
+	adaptor->source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, evts, gio_read_socket, adaptor, gio_poll_destroy);
+
+	/* we are handing the channel off to be managed by mainloop now.
+	 * remove our reference. */
+	g_io_channel_unref(channel);
+
+	return 0;
+}
+
+static int32_t
+my_g_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
+		  void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return 0;
+}
+
+static int32_t
+my_g_dispatch_del(int32_t fd)
+{
+	struct gio_to_qb_poll *adaptor;
+	if (qb_array_index(gio_map, fd, (void **)&adaptor) == 0) {
+		g_source_remove(adaptor->source);
+		adaptor->is_used = FALSE;
+	}
+	return 0;
+}
+#endif /* HAVE_GLIB */
+
+static int32_t
+my_job_add(enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn fn)
+{
+	return qb_loop_job_add(bms_loop, p, data, fn);
+}
+
+static int32_t
+my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
+		void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_add(bms_loop, p, fd, evts, data, fn);
+}
+
+static int32_t
+my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
+		void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_mod(bms_loop, p, fd, evts, data, fn);
+}
+
+static int32_t
+my_dispatch_del(int32_t fd)
+{
+	return qb_loop_poll_del(bms_loop, fd);
+}
+
+int32_t
+main(int32_t argc, char *argv[])
+{
+	const char *options = "mpseugh";
+	int32_t opt;
+	int32_t rc;
+	enum qb_ipc_type ipc_type = QB_IPC_NATIVE;
+	struct qb_ipcs_service_handlers sh = {
+		.connection_accept = s1_connection_accept_fn,
+		.connection_created = s1_connection_created_fn,
+		.msg_process = s1_msg_process_fn,
+		.connection_destroyed = s1_connection_destroyed_fn,
+		.connection_closed = s1_connection_closed_fn,
+	};
+	struct qb_ipcs_poll_handlers ph = {
+		.job_add = my_job_add,
+		.dispatch_add = my_dispatch_add,
+		.dispatch_mod = my_dispatch_mod,
+		.dispatch_del = my_dispatch_del,
+	};
+#ifdef HAVE_GLIB
+	struct qb_ipcs_poll_handlers glib_ph = {
+		.job_add = NULL, /* FIXME */
+		.dispatch_add = my_g_dispatch_add,
+		.dispatch_mod = my_g_dispatch_mod,
+		.dispatch_del = my_g_dispatch_del,
+	};
+#endif /* HAVE_GLIB */
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'm':
+			ipc_type = QB_IPC_SHM;
+			break;
+		case 'u':
+			ipc_type = QB_IPC_SOCKET;
+			break;
+		case 'g':
+			use_glib = QB_TRUE;
+			break;
+		case 'e':
+			use_events = QB_TRUE;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+	signal(SIGINT, sigusr1_handler);
+
+	qb_log_init("ipcserver", LOG_USER, LOG_TRACE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_format_set(QB_LOG_STDERR, "%f:%l [%p] %b");
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	s1 = qb_ipcs_create("ipcserver", 0, ipc_type, &sh);
+	if (s1 == 0) {
+		qb_perror(LOG_ERR, "qb_ipcs_create");
+		exit(1);
+	}
+	/* This forces the clients to use a minimum buffer size */
+	qb_ipcs_enforce_buffer_size(s1, ONE_MEG);
+
+	if (!use_glib) {
+		bms_loop = qb_loop_create();
+		qb_ipcs_poll_handlers_set(s1, &ph);
+		rc = qb_ipcs_run(s1);
+		if (rc != 0) {
+			errno = -rc;
+			qb_perror(LOG_ERR, "qb_ipcs_run");
+			exit(1);
+		}
+		qb_loop_run(bms_loop);
+	} else {
+#ifdef HAVE_GLIB
+		glib_loop = g_main_loop_new(NULL, FALSE);
+		gio_map = qb_array_create_2(16, sizeof(struct gio_to_qb_poll), 1);
+		qb_ipcs_poll_handlers_set(s1, &glib_ph);
+		rc = qb_ipcs_run(s1);
+		if (rc != 0) {
+			errno = -rc;
+			qb_perror(LOG_ERR, "qb_ipcs_run");
+			exit(1);
+		}
+		g_main_loop_run(glib_loop);
+#else
+		qb_log(LOG_ERR,
+		       "You don't seem to have glib-devel installed.\n");
+#endif
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/examples/mapnotify.c b/examples/mapnotify.c
new file mode 100644
index 0000000..daf5164
--- /dev/null
+++ b/examples/mapnotify.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse <jfriesse at redhat.com>
+ *         Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qbmap.h>
+
+static void
+notify_fn(uint32_t event, char *key, void *old_value, void *value,
+	  void *user_data)
+{
+	if (event == QB_MAP_NOTIFY_FREE) {
+		fprintf(stderr, "Notify[FREE] %s [%d]\n",
+			key, *(int *)old_value);
+		free(old_value);
+	} else if (event == QB_MAP_NOTIFY_DELETED) {
+		fprintf(stderr, "Notify[DELETED] %s [%d]\n",
+			key, *(int *)old_value);
+	} else if (event == QB_MAP_NOTIFY_REPLACED) {
+		fprintf(stderr, "Notify[REPLACED] %s [%d] -> [%d]\n",
+			key, *(int *)old_value, *(int *)value);
+	} else {
+		fprintf(stderr, "Notify[%d] %s \n", event, key);
+		if (value != NULL) {
+			fprintf(stderr, " value = [%d]\n", *(int *)value);
+		}
+		if (old_value != NULL) {
+			fprintf(stderr, " old value = [%d]\n",
+				*(int *)old_value);
+		}
+	}
+}
+
+static void
+add_cs_keys(qb_map_t * m)
+{
+	qb_map_put(m, "compatibility", strdup("none"));
+	qb_map_put(m, "totem.version", strdup("2"));
+	qb_map_put(m, "totem.secauth", strdup("off"));
+	qb_map_put(m, "totem.threads", strdup("0"));
+	qb_map_put(m, "totem.interface.ringnumber", strdup("0"));
+	qb_map_put(m, "totem.interface.bindnetaddr", strdup("192.168.122.1"));
+	qb_map_put(m, "totem.interface.mcastaddr", strdup("239.255.1.1"));
+	qb_map_put(m, "totem.interface.mcastport", strdup("5405"));
+	qb_map_put(m, "totem.interface.ttl", strdup("1"));
+	qb_map_put(m, "logging.to_stderr", strdup("yes"));
+	qb_map_put(m, "logging.to_logfile", strdup("no"));
+	qb_map_put(m, "logging.logfile", strdup("/var/log/cluster/corosync.log"));
+	qb_map_put(m, "logging.to_syslog", strdup("no"));
+	qb_map_put(m, "logging.debug", strdup("off"));
+	qb_map_put(m, "logging.timestamp", strdup("on"));
+	qb_map_put(m, "logging.logger_subsys.subsys", strdup("MAIN"));
+	qb_map_put(m, "logging.logger_subsys.debug", strdup("on"));
+	qb_map_put(m, "amf.mode", strdup("disabled"));
+	qb_map_put(m, "quorum.provider", strdup("corosync_quorum_ykd"));
+	qb_map_put(m, "runtime.services.evs.service_id", strdup("0"));
+	qb_map_put(m, "runtime.services.evs.0.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.evs.0.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.service_id", strdup("7"));
+	qb_map_put(m, "runtime.services.cfg.0.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.0.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.1.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.1.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.2.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.2.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.3.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cfg.3.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.service_id", strdup("8"));
+	qb_map_put(m, "runtime.services.cpg.0.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.0.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.1.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.1.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.2.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.2.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.3.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.3.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.4.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.4.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.cpg.5.tx", strdup("1"));
+	qb_map_put(m, "runtime.services.cpg.5.rx", strdup("1"));
+	qb_map_put(m, "runtime.services.confdb.service_id", strdup("11"));
+	qb_map_put(m, "runtime.services.pload.service_id", strdup("13"));
+	qb_map_put(m, "runtime.services.pload.0.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.pload.0.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.pload.1.tx", strdup("0"));
+	qb_map_put(m, "runtime.services.pload.1.rx", strdup("0"));
+	qb_map_put(m, "runtime.services.quorum.service_id", strdup("12"));
+	qb_map_put(m, "runtime.connections.active", strdup("1"));
+	qb_map_put(m, "runtime.connections.closed", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.service_id", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.client_pid", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.responses", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.dispatched", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.requests", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.send_retries", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.recv_retries", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.flow_control", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.flow_control_count", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.queue_size", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.invalid_request", strdup("0"));
+	qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.overload", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.msg_reserved", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.msg_queue_avail", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.orf_token_tx", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.orf_token_rx", strdup("100"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_merge_detect_tx", strdup("29"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_merge_detect_rx", strdup("29"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_join_tx", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_join_rx", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_tx", strdup("13"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_retx", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_rx", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_commit_token_tx", strdup("2"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_commit_token_rx", strdup("2"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.token_hold_cancel_tx", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.token_hold_cancel_rx", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.operational_entered", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.operational_token_lost", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.gather_entered", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.gather_token_lost", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.commit_entered", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.commit_token_lost", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.recovery_entered", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.recovery_token_lost", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.consensus_timeouts", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.mtt_rx_token", strdup("106"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.avg_token_workload", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.avg_backlog_calc", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.rx_msg_dropped", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.continuous_gather", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.firewall_enabled_or_nic_failure", strdup("0"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.ip", strdup("r(0) ip(192.168.122.1) "));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.join_count", strdup("1"));
+	qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.status", strdup("joined"));
+	qb_map_put(m, "runtime.blackbox.dump_flight_data", strdup("no"));
+	qb_map_put(m, "runtime.blackbox.dump_state", strdup("no"));
+}
+
+
+int
+main(void)
+{
+	qb_map_t *trie;
+	int *i1, *i2, *i3;
+	qb_map_iter_t *iter;
+	const char *key;
+	void *val;
+	uint32_t revents = (QB_MAP_NOTIFY_DELETED |
+			    QB_MAP_NOTIFY_REPLACED |
+			    QB_MAP_NOTIFY_INSERTED |
+			    QB_MAP_NOTIFY_RECURSIVE);
+
+	trie = qb_trie_create();
+	assert(trie != NULL);
+	qb_trie_dump(trie);
+	add_cs_keys(trie);
+
+	i1 = malloc(sizeof(int));
+	assert(i1 != NULL);
+	*i1 = 1;
+
+	i2 = malloc(sizeof(int));
+	assert(i2 != NULL);
+	*i2 = 2;
+
+	i3 = malloc(sizeof(int));
+	assert(i3 != NULL);
+	*i3 = 3;
+
+	qb_map_notify_add(trie, NULL, notify_fn, QB_MAP_NOTIFY_FREE, NULL);
+
+	qb_map_put(trie, "test.key1", i1);
+	qb_map_put(trie, "test.key2", i2);
+
+	qb_map_notify_add(trie, "test.", notify_fn, revents, NULL);
+	qb_trie_dump(trie);
+
+	qb_map_put(trie, "test.key1", i3);
+
+	iter = qb_map_pref_iter_create(trie, "test.");
+	while ((key = qb_map_iter_next(iter, &val)) != NULL) {
+		fprintf(stderr, "Iter %s [%d]\n", key, *(int *)val);
+		qb_map_rm(trie, key);
+	}
+	qb_map_iter_free(iter);
+	qb_map_notify_del_2(trie, "test.", notify_fn, revents, NULL);
+	qb_map_destroy(trie);
+
+	return (0);
+}
diff --git a/examples/simplelog.c b/examples/simplelog.c
new file mode 100644
index 0000000..784f1af
--- /dev/null
+++ b/examples/simplelog.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+#include <syslog.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+
+#define MY_TAG_ONE   (1)
+#define MY_TAG_TWO   (1 << 1)
+#define MY_TAG_THREE (1 << 2)
+
+static uint8_t _log_priority = LOG_WARNING;
+
+static void
+func_one(void)
+{
+	FILE *fd;
+
+	qb_enter();
+	qb_logt(LOG_DEBUG, MY_TAG_TWO, "arf arf?");
+	qb_logt(LOG_CRIT, MY_TAG_THREE, "arrrg!");
+	qb_logt(134, MY_TAG_THREE, "big priority");
+	qb_logt(LOG_ERR, MY_TAG_THREE, "oops, I did it again");
+	qb_log(LOG_INFO, "are you aware ...");
+
+	fd = fopen("/nothing.txt", "r+");
+	if (fd == NULL) {
+		qb_perror(LOG_ERR, "can't open(\"/nothing.txt\")");
+	} else {
+		fclose(fd);
+	}
+	qb_leave();
+}
+
+static void
+func_two(void)
+{
+	qb_enter();
+	qb_logt(LOG_DEBUG, 0, "arf arf?");
+	qb_logt(LOG_CRIT, MY_TAG_ONE, "arrrg!");
+	qb_log(LOG_ERR, "oops, I did it again");
+	qb_logt(LOG_INFO, MY_TAG_THREE, "are you aware ...");
+	qb_leave();
+}
+
+static void
+show_usage(const char *name)
+{
+	printf("usage: \n");
+	printf("%s <options>\n", name);
+	printf("\n");
+	printf("  options:\n");
+	printf("\n");
+	printf("  -v             verbose\n");
+	printf("  -t             threaded logging\n");
+	printf("  -e             log to stderr\n");
+	printf("  -b             log to blackbox\n");
+	printf("  -f <filename>  log to a file\n");
+	printf("  -h             show this help text\n");
+	printf("\n");
+}
+
+static int32_t do_blackbox = QB_FALSE;
+static int32_t do_threaded = QB_FALSE;
+
+static void
+sigsegv_handler(int sig)
+{
+	(void)signal(SIGSEGV, SIG_DFL);
+	if (do_blackbox) {
+		qb_log_blackbox_write_to_file("simple-log.fdata");
+	}
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
+	raise(SIGSEGV);
+}
+
+static const char *
+my_tags_stringify(uint32_t tags)
+{
+	if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
+		return "libqb";
+	} else if (qb_bit_is_set(tags, 0)) {
+		return "ONE";
+	} else if (qb_bit_is_set(tags, 1)) {
+		return "TWO";
+	} else if (qb_bit_is_set(tags, 2)) {
+		return "THREE";
+	} else {
+		return "MAIN";
+	}
+}
+
+static void
+trace_logger(int32_t t,
+	     struct qb_log_callsite *cs, time_t timestamp, const char *msg)
+{
+	char output_buffer[QB_LOG_MAX_LEN];
+	output_buffer[0] = '\0';
+	qb_log_target_format(t, cs, timestamp, msg, output_buffer);
+	fprintf(stderr, "%s\n", output_buffer);
+}
+
+static void
+m_filter(struct qb_log_callsite *cs)
+{
+	if ((cs->priority >= LOG_ALERT &&
+	     cs->priority <= _log_priority) &&
+	    strcmp(cs->filename, __FILE__) == 0) {
+		qb_bit_set(cs->targets, QB_LOG_STDERR);
+	} else {
+		qb_bit_clear(cs->targets, QB_LOG_STDERR);
+	}
+}
+
+int32_t
+main(int32_t argc, char *argv[])
+{
+	const char *options = "vhteobdf:";
+	int32_t opt;
+	int32_t tracer;
+	int32_t do_stderr = QB_FALSE;
+	int32_t do_stdout = QB_FALSE;
+	int32_t do_dump_blackbox = QB_FALSE;
+	char *logfile = NULL;
+	int32_t log_fd = -1;
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'd':
+			do_dump_blackbox = QB_TRUE;
+			break;
+		case 't':
+			do_threaded = QB_TRUE;
+			break;
+		case 'e':
+			do_stderr = QB_TRUE;
+			break;
+		case 'o':
+			do_stdout = QB_TRUE;
+			break;
+		case 'b':
+			do_blackbox = QB_TRUE;
+			break;
+		case 'f':
+			logfile = optarg;
+			break;
+		case 'v':
+			_log_priority++;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+
+	if (do_dump_blackbox) {
+		qb_log_blackbox_print_from_file("simple-log.fdata");
+		exit(0);
+	}
+
+	signal(SIGSEGV, sigsegv_handler);
+
+	qb_log_init("simple-log", LOG_USER, LOG_INFO);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, do_threaded);
+	qb_log_tags_stringify_fn_set(my_tags_stringify);
+
+	if (do_stderr) {
+		qb_log_filter_fn_set(m_filter);
+		qb_log_format_set(QB_LOG_STDERR, "[%p] %4g: %f:%l %b");
+		qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+		tracer = qb_log_custom_open(trace_logger, NULL, NULL, NULL);
+		qb_log_ctl(tracer, QB_LOG_CONF_ENABLED, QB_TRUE);
+		qb_log_format_set(tracer, "%4g: %n() %b");
+		qb_log_filter_ctl2(tracer, QB_LOG_FILTER_ADD,
+				   QB_LOG_FILTER_FILE, __FILE__,
+				   LOG_TRACE, 200);
+	}
+	if (do_stdout) {
+		qb_log_filter_ctl2(QB_LOG_STDOUT, QB_LOG_FILTER_ADD,
+				   QB_LOG_FILTER_FILE, __FILE__,
+				   LOG_ALERT, QB_MIN(LOG_DEBUG, _log_priority));
+		qb_log_format_set(QB_LOG_STDOUT, "[%p] %4g: %f:%l %b");
+		qb_log_ctl(QB_LOG_STDOUT, QB_LOG_CONF_ENABLED, QB_TRUE);
+	}
+	if (do_blackbox) {
+		qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
+				  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 4096);
+		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_THREADED, QB_FALSE);
+		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+	}
+	if (logfile) {
+		log_fd = qb_log_file_open(logfile);
+		qb_log_filter_ctl(log_fd, QB_LOG_FILTER_ADD,
+				  QB_LOG_FILTER_FILE, __FILE__, _log_priority);
+		qb_log_format_set(log_fd, "[%N] %t %n() [%p] %b");
+		qb_log_ctl(log_fd, QB_LOG_CONF_THREADED, do_threaded);
+		qb_log_ctl(log_fd, QB_LOG_CONF_ENABLED, QB_TRUE);
+	}
+	if (do_threaded) {
+		qb_log_thread_start();
+	}
+	qb_log(LOG_DEBUG, "hello");
+	qb_log(LOG_INFO, "this is an info");
+	qb_log(LOG_NOTICE, "hello - notice?");
+	{
+		char * str = NULL;
+		qb_log(LOG_ERR,
+		       "%s-%d-%s-%u",
+		       NULL, 952, str, 56);
+	}
+	func_one();
+	func_two();
+
+	if (!do_threaded) {
+		/* Disabling syslog here will prevent the logs from
+		 * getting flushed in qb_log_fini() if threaded
+		 * logging is on.
+		 */
+		qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+		qb_log(LOG_WARNING, "no syslog");
+		qb_log(LOG_ERR, "no syslog");
+	}
+
+	if (do_blackbox) {
+		logfile = NULL;
+		logfile[5] = 'a';
+	} else {
+		qb_log_fini();
+	}
+	return 0;
+}
diff --git a/examples/tcpclient.c b/examples/tcpclient.c
new file mode 100644
index 0000000..db4d8bb
--- /dev/null
+++ b/examples/tcpclient.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif /* HAVE_NETDB_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+
+int
+main(int argc, char *argv[])
+{
+	int sock;
+	int32_t res;
+	char send_data[1024];
+	char recv_data[1024];
+	char *newline;
+	struct sockaddr_in server_addr;
+	struct hostent *host = gethostbyname("127.0.0.1");
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+		perror("Socket");
+		exit(1);
+	}
+
+	server_addr.sin_family = AF_INET;
+	server_addr.sin_port = htons(5000);
+	memcpy(&server_addr.sin_addr, host->h_addr_list[0],
+	       sizeof(server_addr.sin_addr));
+	bzero(&(server_addr.sin_zero), 8);
+
+	if (connect(sock, (struct sockaddr *)&server_addr,
+		    sizeof(struct sockaddr)) == -1) {
+		perror("Connect");
+		exit(1);
+	}
+
+	while (1) {
+		printf("\nSEND (q or Q to quit) : ");
+		if (fgets(send_data, 1024, stdin) == NULL) {
+			continue;
+		}
+		newline = strrchr(send_data, '\n');
+		if (newline) {
+			*newline = '\0';
+		}
+		res = send(sock, send_data, strlen(send_data), 0);
+		if (strcasecmp(send_data, "q") == 0) {
+			close(sock);
+			printf("you typed QUIT\n");
+			break;
+		}
+
+		if (res > 0) {
+			res = recv(sock, recv_data, 1024, 0);
+			if (res > 0) {
+				recv_data[res] = '\0';
+				printf("\nResponse: %s ", recv_data);
+			} else {
+				perror("recv");
+			}
+		}
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/examples/tcpserver.c b/examples/tcpserver.c
new file mode 100644
index 0000000..88210e9
--- /dev/null
+++ b/examples/tcpserver.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif /* HAVE_NETDB_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif /* HAVE_SYS_POLL_H */
+
+#include <qb/qbdefs.h>
+#include <qb/qbloop.h>
+
+static int32_t
+sock_read_fn(int32_t fd, int32_t revents, void *data)
+{
+	char recv_data[1024];
+	char send_data[1024];
+	int bytes_recieved;
+
+	if (revents & POLLHUP) {
+		printf("Socket %d peer closed\n", fd);
+		close(fd);
+		return QB_FALSE;
+	}
+
+	bytes_recieved = recv(fd, recv_data, 1024, 0);
+	if (bytes_recieved < 0) {
+		perror("recv");
+		return QB_TRUE;
+	}
+	recv_data[bytes_recieved] = '\0';
+
+	if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0) {
+		printf("Quiting connection from socket %d\n", fd);
+		close(fd);
+		return QB_FALSE;
+	} else {
+		printf("Recieved: %s\n", recv_data);
+		snprintf(send_data, 1024, "ACK %d bytes", bytes_recieved);
+		if (send(fd, send_data, strlen(send_data), 0) < 0) {
+			close(fd);
+			return QB_FALSE;
+		}
+	}
+	return QB_TRUE;
+}
+
+static int32_t
+sock_accept_fn(int32_t fd, int32_t revents, void *data)
+{
+	struct sockaddr_in client_addr;
+	qb_loop_t *ml = (qb_loop_t *) data;
+	socklen_t sin_size = sizeof(struct sockaddr_in);
+	int connected = accept(fd, (struct sockaddr *)&client_addr, &sin_size);
+
+	if (connected < 0) {
+		perror("accept");
+		return QB_TRUE;
+	}
+	printf("I got a connection from (%s , %d)\n",
+	       inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
+
+	qb_loop_poll_add(ml, QB_LOOP_MED, connected, POLLIN, ml, sock_read_fn);
+
+	return QB_TRUE;
+}
+
+static int32_t
+please_exit_fn(int32_t rsignal, void *data)
+{
+	qb_loop_t *ml = (qb_loop_t *) data;
+
+	printf("Shutting down at you request...\n");
+	qb_loop_stop(ml);
+	return QB_FALSE;
+}
+
+int
+main(int argc, char *argv[])
+{
+	int sock;
+	int true_opt = 1;
+	struct sockaddr_in server_addr;
+	qb_loop_t *ml = qb_loop_create();
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+		perror("Socket");
+		exit(1);
+	}
+
+	if (setsockopt(sock,
+		       SOL_SOCKET,
+		       SO_REUSEADDR, &true_opt, sizeof(int)) == -1) {
+		perror("Setsockopt");
+		exit(1);
+	}
+
+	server_addr.sin_family = AF_INET;
+	server_addr.sin_port = htons(5000);
+	server_addr.sin_addr.s_addr = INADDR_ANY;
+	bzero(&(server_addr.sin_zero), 8);
+
+	printf("TCPServer binding to port 5000\n");
+	if (bind(sock,
+		 (struct sockaddr *)&server_addr,
+		 sizeof(struct sockaddr)) == -1) {
+		perror("Unable to bind");
+		exit(1);
+	}
+
+	printf("TCPServer Waiting for client on port 5000\n");
+
+	if (listen(sock, 5) == -1) {
+		perror("Listen");
+		exit(1);
+	}
+
+	qb_loop_poll_add(ml, QB_LOOP_MED, sock, POLLIN, ml, sock_accept_fn);
+
+	qb_loop_signal_add(ml, QB_LOOP_HIGH, SIGINT, ml, please_exit_fn, NULL);
+	qb_loop_run(ml);
+
+	close(sock);
+	return 0;
+}
diff --git a/include/.gitignore b/include/.gitignore
new file mode 100644
index 0000000..8c67c55
--- /dev/null
+++ b/include/.gitignore
@@ -0,0 +1,5 @@
+config.h
+config.h.in
+qb/qbconfig.h
+qb/stamp-h2
+stamp-h1
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..7e5fe38
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,27 @@
+# Copyright (c) 2010 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+MAINTAINERCLEANFILES    = Makefile.in config.h.in
+
+EXTRA_DIST 		= $(noinst_HEADERS)
+noinst_HEADERS          = os_base.h tlist.h
+
+SUBDIRS			= qb
+
diff --git a/include/os_base.h b/include/os_base.h
new file mode 100644
index 0000000..82e982b
--- /dev/null
+++ b/include/os_base.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_OS_BASE_H_DEFINED
+#define QB_OS_BASE_H_DEFINED
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif /* HAVE_SYS_UIO_H */
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <assert.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#ifndef S_SPLINT_S
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#endif /* S_SPLINT_S */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif /*HAVE_ERRNO_H*/
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif /* HAVE_SYS_STAT_H */
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+
+#ifndef S_SPLINT_S
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif /* HAVE_SYSLOG_H */
+#endif /* S_SPLINT_S */
+
+#if defined HAVE_CLOCK_GETTIME && defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
+#define HAVE_MONOTONIC_CLOCK 1
+#endif /* have monotonic clock */
+
+#ifdef HAVE_EPOLL_CREATE1
+#define HAVE_EPOLL 1
+#endif /* HAVE_EPOLL_CREATE */
+
+#if defined(__UCLIBC__)
+ #define DISABLE_POSIX_THREAD_PROCESS_SHARED 1
+#endif
+
+/* The pshared semaphore madness:
+ * To have a usable pshared semaphore we need the timed_wait api
+ * and pshared functionality.
+ *
+ * The order of choice is:
+ * 1) real posix sem -> HAVE_POSIX_PSHARED_SEMAPHORE
+ * 2) sysv sems (if we have semtimedop) -> HAVE_SYSV_PSHARED_SEMAPHORE
+ * 3) faked sems using pthread_cond_timedwait -> HAVE_RPL_PSHARED_SEMAPHORE
+ * 4) ENOTSUP
+ */
+#undef HAVE_POSIX_PSHARED_SEMAPHORE
+#undef HAVE_SYSV_PSHARED_SEMAPHORE
+#undef HAVE_RPL_PSHARED_SEMAPHORE
+
+#if defined(DISABLE_POSIX_THREAD_PROCESS_SHARED)
+ #undef HAVE_PTHREAD_SHARED_SPIN_LOCK
+#endif /* DISABLE_POSIX_THREAD_PROCESS_SHARED */
+
+#if  !defined(DISABLE_POSIX_THREAD_PROCESS_SHARED) && \
+     _POSIX_THREAD_PROCESS_SHARED > 0
+
+  #if defined(HAVE_PTHREAD_SPIN_LOCK)
+  #define HAVE_PTHREAD_SHARED_SPIN_LOCK 1
+  #endif /* HAVE_PTHREAD_SPIN_LOCK */
+
+  #if  defined(HAVE_SEM_TIMEDWAIT)
+  #define HAVE_POSIX_PSHARED_SEMAPHORE 1
+  #else
+    #if defined(HAVE_PTHREAD_CONDATTR_SETPSHARED) && \
+        defined(HAVE_PTHREAD_MUTEXATTR_SETPSHARED)
+    #define HAVE_RPL_PSHARED_SEMAPHORE 1
+    #endif
+  #endif /* HAVE_SEM_TIMEDWAIT */
+#endif /* posix pshared */
+
+#ifdef HAVE_SEMTIMEDOP
+#define HAVE_SYSV_PSHARED_SEMAPHORE 1
+#endif /* HAVE_SEM_TIMEDWAIT */
+
+#if !defined(HAVE_SYSV_PSHARED_SEMAPHORE) && \
+    !defined(HAVE_POSIX_PSHARED_SEMAPHORE) && \
+    !defined(HAVE_RPL_PSHARED_SEMAPHORE)
+#define DISABLE_IPC_SHM 1
+#endif /* HAVE PSHARED SEMAPHORE */
+
+#ifndef HAVE_STRCHRNUL
+char *strchrnul (const char *s, int c_in);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dest, const char *src, size_t maxlen);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dest, const char *src, size_t maxlen);
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#endif /* QB_OS_BASE_H_DEFINED */
diff --git a/include/qb/Makefile.am b/include/qb/Makefile.am
new file mode 100644
index 0000000..41d7e23
--- /dev/null
+++ b/include/qb/Makefile.am
@@ -0,0 +1,26 @@
+# Copyright (c) 2010 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+MAINTAINERCLEANFILES    = Makefile.in
+instdir 		= $(includedir)/qb/
+inst_HEADERS		= qbhdb.h qblist.h qbdefs.h qbatomic.h \
+			  qbloop.h qbrb.h qbutil.h qbarray.h \
+			  qbipcc.h qbipcs.h qbipc_common.h qblog.h \
+			  qbconfig.h qbmap.h
diff --git a/include/qb/qbarray.h b/include/qb/qbarray.h
new file mode 100644
index 0000000..d86407e
--- /dev/null
+++ b/include/qb/qbarray.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_ARRAY_H_DEFINED
+#define QB_ARRAY_H_DEFINED
+
+#include <stdint.h>
+#ifndef S_SPLINT_S
+#include <unistd.h>
+#endif /* S_SPLINT_S */
+#include <qb/qbdefs.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @file qbarray.h
+ * This is a dynamic array (it can grow, but without moving memory).
+ *
+ * @code
+ * arr = qb_array_create_2(64, sizeof(struct my_struct), 256);
+ * ...
+ * res = qb_array_index(arr, idx, (void**)&my_ptr);
+ * if (res < 0) {
+ *	return res;
+ * }
+ * // use my_ptr, now even if there is a grow, this pointer will be valid.
+ * @endcode
+ */
+
+struct qb_array;
+
+/**
+ * This is an opaque data type representing an instance of an array.
+ */
+typedef struct qb_array qb_array_t;
+
+/**
+ * Create an array with fixed sized elements.
+ *
+ * @param max_elements initial max elements.
+ * @param element_size size of each element.
+ * @return array instance.
+ */
+qb_array_t* qb_array_create(size_t max_elements, size_t element_size);
+
+/**
+ * Create an array with fixed sized elements.
+ *
+ * @param max_elements initial max elements.
+ * @param element_size size of each element.
+ * @param autogrow_elements the number of elements to grow automatically by.
+ *
+ * @return array instance.
+ */
+qb_array_t* qb_array_create_2(size_t max_elements, size_t element_size,
+                              size_t autogrow_elements);
+
+
+/**
+ * Get an element at a particular index.
+ * @param a array instance.
+ * @param idx the index
+ * @param element_out the pointer to the element data.
+ * @return (0 == success, else -errno)
+ */
+int32_t qb_array_index(qb_array_t* a, int32_t idx, void** element_out);
+
+/**
+ * Grow the array.
+ *
+ * @param a array instance.
+ * @param max_elements the new maximum size of the array.
+ * @return (0 == success, else -errno)
+ */
+int32_t qb_array_grow(qb_array_t* a, size_t max_elements);
+
+/**
+ * Get the number of bins used or the array.
+ */
+size_t qb_array_num_bins_get(qb_array_t* a);
+
+/**
+ * Get the number of elements per bin.
+ */
+size_t qb_array_elems_per_bin_get(qb_array_t* a);
+
+
+typedef void (*qb_array_new_bin_cb_fn)(qb_array_t * a, uint32_t bin);
+
+/**
+ * Get a callback when a new bin is allocated.
+ */
+int32_t qb_array_new_bin_cb_set(qb_array_t * a, qb_array_new_bin_cb_fn fn);
+
+/**
+ * Free all the memory used by the array.
+ * @param a array instance.
+ */
+void qb_array_free(qb_array_t * a);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_ARRAY_H_DEFINED */
diff --git a/include/qb/qbatomic.h b/include/qb/qbatomic.h
new file mode 100644
index 0000000..bd475b4
--- /dev/null
+++ b/include/qb/qbatomic.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2003 Sebastian Wilhelmi
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Copied from the glib code base (glib/gatomic.h) and namespaced
+ * for libqb.
+ */
+
+#ifndef QB_ATOMIC_H_DEFINED
+#define QB_ATOMIC_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <stdint.h>
+#include <qb/qbdefs.h>
+#include <qb/qbconfig.h>
+
+/**
+ * @file
+ * Basic atomic integer and pointer operations
+ *
+ * The following functions can be used to atomically access integers and
+ * pointers. They are implemented as inline assembler function on most
+ * platforms and use slower fall-backs otherwise. Using them can sometimes
+ * save you from using a performance-expensive pthread_mutex to protect the
+ * integer or pointer.
+ *
+ * The most important usage is reference counting. Using
+ * qb_atomic_int_inc() and qb_atomic_int_dec_and_test() makes reference
+ * counting a very fast operation.
+ *
+ * You must not directly read integers or pointers concurrently
+ * accessed by multiple threads, but use the atomic accessor functions
+ * instead. That is, always use qb_atomic_int_get() and qb_atomic_pointer_get()
+ * for read outs. They provide the neccessary synchonization mechanisms
+ * like memory barriers to access memory locations concurrently.
+ *
+ * If you are using those functions for anything apart from
+ * simple reference counting, you should really be aware of the implications
+ * of doing that. There are literally thousands of ways to shoot yourself
+ * in the foot. So if in doubt, use a pthread_mutex. If you don't know, what
+ * memory barriers are, do not use anything but qb_atomic_int_inc() and
+ * qb_atomic_int_dec_and_test().
+ *
+ * It is not safe to set an integer or pointer just by assigning
+ * to it, when it is concurrently accessed by other threads with the following
+ * functions. Use qb_atomic_int_compare_and_exchange() or
+ * qb_atomic_pointer_compare_and_exchange() respectively.
+ */
+
+void qb_atomic_init(void);
+
+/**
+ * Atomically adds val to the integer pointed to by atomic.
+ * It returns the value of *atomic just before the addition
+ * took place. Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param val the value to add to *atomic
+ * @return the value of *atomic before the addition.
+ */
+int32_t qb_atomic_int_exchange_and_add(volatile int32_t QB_GNUC_MAY_ALIAS *
+				      atomic, int32_t val);
+/**
+ * Atomically adds val to the integer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param val the value to add to *atomic
+ */
+void qb_atomic_int_add(volatile int32_t QB_GNUC_MAY_ALIAS * atomic, int32_t val);
+
+/**
+ * Compares oldval with the integer pointed to by atomic and
+ * if they are equal, atomically exchanges *atomic with newval.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param oldval the assumed old value of *atomic
+ * @param newval the new value of *atomic
+ *
+ * @return QB_TRUE, if *atomic was equal oldval. QB_FALSE otherwise.
+ */
+int32_t qb_atomic_int_compare_and_exchange(volatile int32_t QB_GNUC_MAY_ALIAS *
+					  atomic, int32_t oldval,
+					  int32_t newval);
+
+/**
+ * Compares oldval with the pointer pointed to by atomic and
+ * if they are equal, atomically exchanges *atomic with newval.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to a void*
+ * @param oldval the assumed old value of *atomic
+ * @param newval the new value of *atomic
+ *
+ * @return QB_TRUE if atomic was equal oldval, else QB_FALSE.
+ */
+int32_t qb_atomic_pointer_compare_and_exchange(volatile void* QB_GNUC_MAY_ALIAS
+					      * atomic, void* oldval,
+					      void* newval);
+
+/**
+ * Reads the value of the integer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ *
+ * @return the value of atomic
+ */
+int32_t qb_atomic_int_get(volatile int32_t QB_GNUC_MAY_ALIAS * atomic);
+
+/**
+ * Sets the value of the integer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param newval the new value
+ */
+void qb_atomic_int_set(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+		      int32_t newval);
+
+/**
+ * Reads the value of the pointer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to a void*.
+ * @return the value to add to atomic.
+ */
+void* qb_atomic_pointer_get(volatile void* QB_GNUC_MAY_ALIAS * atomic);
+
+/**
+ * Sets the value of the pointer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to a void*
+ * @param newval the new value
+ *
+ */
+void qb_atomic_pointer_set(volatile void* QB_GNUC_MAY_ALIAS * atomic,
+			  void* newval);
+
+#ifndef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+#define qb_atomic_int_get(atomic) 		((int32_t)*(atomic))
+#define qb_atomic_int_set(atomic, newval) 	((void) (*(atomic) = (newval)))
+#define qb_atomic_pointer_get(atomic) 		((void*)*(atomic))
+#define qb_atomic_pointer_set(atomic, newval)	((void*) (*(atomic) = (newval)))
+#else
+#define qb_atomic_int_get(atomic) \
+ ((void) sizeof (char* [sizeof (*(atomic)) == sizeof (int32_t) ? 1 : -1]), \
+  (qb_atomic_int_get) ((volatile int32_t QB_GNUC_MAY_ALIAS *) (volatile void *) (atomic)))
+#define qb_atomic_int_set(atomic, newval) \
+ ((void) sizeof (char* [sizeof (*(atomic)) == sizeof (int32_t) ? 1 : -1]), \
+  (qb_atomic_int_set) ((volatile int32_t QB_GNUC_MAY_ALIAS *) (volatile void *) (atomic), (newval)))
+#define qb_atomic_pointer_get(atomic) \
+ ((void) sizeof (char* [sizeof (*(atomic)) == sizeof (void*) ? 1 : -1]), \
+  (qb_atomic_pointer_get) ((volatile void* QB_GNUC_MAY_ALIAS *) (volatile void *) (atomic)))
+#define qb_atomic_pointer_set(atomic, newval) \
+ ((void) sizeof (char* [sizeof (*(atomic)) == sizeof (void*) ? 1 : -1]), \
+  (qb_atomic_pointer_set) ((volatile void* QB_GNUC_MAY_ALIAS *) (volatile void *) (atomic), (newval)))
+#endif /* QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
+
+/**
+ * Atomically increments the integer pointed to by atomic by 1.
+ *
+ * @param atomic a pointer to an integer.
+ */
+#define qb_atomic_int_inc(atomic) (qb_atomic_int_add ((atomic), 1))
+
+/**
+ * Atomically decrements the integer pointed to by atomic by 1.
+ *
+ * @param atomic a pointer to an integer
+ *
+ * @return QB_TRUE if the integer pointed to by atomic is 0
+ *     after decrementing it
+ */
+#define qb_atomic_int_dec_and_test(atomic) \
+  (qb_atomic_int_exchange_and_add ((atomic), -1) == 1)
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_ATOMIC_H_DEFINED */
diff --git a/include/qb/qbconfig.h.in b/include/qb/qbconfig.h.in
new file mode 100644
index 0000000..254e0f8
--- /dev/null
+++ b/include/qb/qbconfig.h.in
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_CONFIG_H_DEFINED
+#define QB_CONFIG_H_DEFINED
+
+/* need atomic memory barrier */
+#undef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+
+/* Enabling code using __attribute__((section)) */
+#undef QB_HAVE_ATTRIBUTE_SECTION
+
+#endif /* QB_CONFIG_H_DEFINED */
diff --git a/include/qb/qbdefs.h b/include/qb/qbdefs.h
new file mode 100644
index 0000000..21d3627
--- /dev/null
+++ b/include/qb/qbdefs.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_DEFS_H_DEFINED
+#define QB_DEFS_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @file qbdefs.h
+ * @author Angus Salkeld <asalkeld at redhat.com>
+ *
+ * These are some convience macros and defines.
+ */
+
+/*
+ * simple math macros
+ */
+#define QB_ROUNDUP(x, y)	((((x) + ((y) - 1)) / (y)) * (y))
+#define QB_ABS(i)		(((i) < 0) ? -(i) : (i))
+#define QB_MAX(a, b)		(((a) > (b)) ? (a) : (b))
+#define QB_MIN(a, b)		(((a) < (b)) ? (a) : (b))
+
+/*
+ * the usual boolean defines
+ */
+#define	QB_FALSE		0
+#define	QB_TRUE			(!QB_FALSE)
+
+/*
+ * bit manipulation
+ */
+#define qb_bit_value(bit) (1 << (bit))
+#define qb_bit_set(barray, bit) (barray |= qb_bit_value(bit))
+#define qb_bit_clear(barray, bit) (barray &= ~(qb_bit_value(bit)))
+#define qb_bit_is_set(barray, bit) (barray & qb_bit_value(bit))
+#define qb_bit_is_clear(barray, bit) (!(barray & qb_bit_value(bit)))
+
+
+/*
+ * handy time based converters.
+ */
+#ifndef HZ
+#define HZ 100			/* 10ms */
+#endif
+
+#define QB_TIME_MS_IN_SEC   1000ULL
+#define QB_TIME_US_IN_SEC   1000000ULL
+#define QB_TIME_NS_IN_SEC   1000000000ULL
+#define QB_TIME_US_IN_MSEC  1000ULL
+#define QB_TIME_NS_IN_MSEC  1000000ULL
+#define QB_TIME_NS_IN_USEC  1000ULL
+
+
+#if defined (__GNUC__) && defined (__STRICT_ANSI__)
+#undef inline
+#define inline __inline__
+#undef typeof
+#define typeof __typeof__
+#endif /* ANSI */
+
+#if  __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define QB_GNUC_DEPRECATED                            \
+  __attribute__((__deprecated__))
+#else
+#define QB_GNUC_DEPRECATED
+#endif /* __GNUC__ */
+
+#if    __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define QB_GNUC_DEPRECATED_FOR(f)                        \
+  __attribute__((deprecated("Use " #f " instead")))
+#else
+#define QB_GNUC_DEPRECATED_FOR(f)        QB_GNUC_DEPRECATED
+#endif /* __GNUC__ */
+
+#if     __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define QB_GNUC_MAY_ALIAS __attribute__((may_alias))
+#else
+#define QB_GNUC_MAY_ALIAS
+#endif
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+
+#endif /* QB_DEFS_H_DEFINED */
diff --git a/include/qb/qbhdb.h b/include/qb/qbhdb.h
new file mode 100644
index 0000000..d9cb83e
--- /dev/null
+++ b/include/qb/qbhdb.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_HDB_H_DEFINED
+#define QB_HDB_H_DEFINED
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <qb/qbarray.h>
+
+/**
+ * @file qbhdb.h
+ * The handle database is for reference counting objects.
+ */
+
+/**
+ * Generic handle type is 64 bits.
+ */
+typedef uint64_t qb_handle_t;
+
+/*
+ * Formatting for string printing on 32/64 bit systems
+ */
+#define QB_HDB_D_FORMAT "%"PRIu64
+#define QB_HDB_X_FORMAT "%"PRIx64
+
+struct qb_hdb_handle {
+	int32_t state;
+	void *instance;
+	int32_t check;
+	int32_t ref_count;
+};
+
+struct qb_hdb {
+	uint32_t handle_count;
+	qb_array_t *handles;
+	uint32_t iterator;
+	void (*destructor) (void *);
+	uint32_t first_run;
+};
+
+/**
+ * Convience macro for declaring a file scoped handle database.
+ * @code
+ * QB_HDB_DECLARE(my_handle_database, NULL);
+ * @endcode
+ */
+#define QB_HDB_DECLARE(database_name,destructor_function)		\
+static struct qb_hdb (database_name) = {				\
+	.handle_count	= 0,						\
+	.handles	= NULL,						\
+	.iterator	= 0,						\
+	.destructor	= destructor_function,				\
+	.first_run	= QB_TRUE					\
+};									\
+
+/**
+ * Create a new database.
+ * @param hdb the database to init.
+ */
+void qb_hdb_create(struct qb_hdb *hdb);
+
+/**
+ * Destroy a handle database.
+ * @param hdb the database to destroy.
+ */
+void qb_hdb_destroy(struct qb_hdb *hdb);
+
+/**
+ * Create a new handle.
+ * @param hdb the database instance
+ * @param instance_size size of the object to malloc
+ * @param handle_id_out new handle
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_handle_create(struct qb_hdb *hdb, int32_t instance_size,
+			     qb_handle_t * handle_id_out);
+/**
+ * Get the instance associated with this handle and increase it's refcount.
+ * @param handle_in the handle
+ * @param hdb the database instance
+ * @param instance (out) pointer to the desired object.
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_handle_get(struct qb_hdb *hdb, qb_handle_t handle_in,
+			  void **instance);
+/**
+ * Get the instance associated with this handle and increase it's refcount.
+ * @param handle_in the handle
+ * @param hdb the database instance
+ * @param instance (out) pointer to the desired object.
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_handle_get_always(struct qb_hdb *hdb, qb_handle_t handle_in,
+				 void **instance);
+/**
+ * Put the instance associated with this handle and decrease it's refcount.
+ * @param handle_in the handle
+ * @param hdb the database instance
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_handle_put(struct qb_hdb *hdb, qb_handle_t handle_in);
+
+/**
+ * Request the destruction of the object.
+ * 
+ * When the refcount is 0, it will be destroyed.
+ *
+ * @param handle_in the handle
+ * @param hdb the database instance
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_handle_destroy(struct qb_hdb *hdb, qb_handle_t handle_in);
+
+/**
+ * Get the current refcount.
+ * @param handle_in the handle
+ * @param hdb the database instance
+ * @return (>= 0 is the refcount, -errno faliure)
+ */
+int32_t qb_hdb_handle_refcount_get(struct qb_hdb *hdb, qb_handle_t handle_in);
+
+/**
+ * Reset the iterator.
+ * @param hdb the database instance
+ */
+void qb_hdb_iterator_reset(struct qb_hdb *hdb);
+
+/**
+ * Get the next object and increament it's refcount.
+ *
+ * Remember to call qb_hdb_handle_put()
+ *
+ * @param hdb the database instance
+ * @param handle (out) the handle
+ * @param instance (out) pointer to the desired object.
+ * @return (0 == ok, -errno faliure)
+ */
+int32_t qb_hdb_iterator_next(struct qb_hdb *hdb, void **instance,
+			     qb_handle_t * handle);
+
+uint32_t qb_hdb_base_convert(qb_handle_t handle);
+uint64_t qb_hdb_nocheck_convert(uint32_t handle);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+#endif /* QB_HDB_H_DEFINED */
diff --git a/include/qb/qbipc_common.h b/include/qb/qbipc_common.h
new file mode 100644
index 0000000..0a4118c
--- /dev/null
+++ b/include/qb/qbipc_common.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_IPC_COMMON_H_DEFINED
+#define QB_IPC_COMMON_H_DEFINED
+
+#include <stdint.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+struct qb_ipc_request_header {
+	int32_t id __attribute__ ((aligned(8)));
+	int32_t size __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8)));
+
+struct qb_ipc_response_header {
+	int32_t id __attribute__ ((aligned(8)));
+	int32_t size __attribute__ ((aligned(8)));
+	int32_t error __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8)));
+
+enum qb_ipc_type {
+	QB_IPC_SOCKET,
+	QB_IPC_SHM,
+	QB_IPC_POSIX_MQ,
+	QB_IPC_SYSV_MQ,
+	QB_IPC_NATIVE,
+};
+
+
+#define QB_IPC_MSG_NEW_MESSAGE 0
+#define QB_IPC_MSG_USER_START QB_IPC_MSG_NEW_MESSAGE
+#define QB_IPC_MSG_AUTHENTICATE -1
+#define QB_IPC_MSG_NEW_EVENT_SOCK -2
+#define QB_IPC_MSG_DISCONNECT -3
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_IPC_COMMON_H_DEFINED */
diff --git a/include/qb/qbipcc.h b/include/qb/qbipcc.h
new file mode 100644
index 0000000..77631e0
--- /dev/null
+++ b/include/qb/qbipcc.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2006-2007, 2009 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_IPCC_H_DEFINED
+#define QB_IPCC_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <qb/qbconfig.h>
+
+#include <pthread.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <qb/qbhdb.h>
+#include <qb/qbipc_common.h>
+
+/**
+ * @file qbipcc.h
+ *
+ * Client IPC API.
+ *
+ * @par Lifecycle of an IPC connection.
+ * An IPC connection is made to the server with qb_ipcc_connect(). This function
+ * connects to the server and requests channels be created for communication.
+ * To disconnect, the client either exits or executes the function qb_ipcc_disconnect().
+ *
+ * @par Synchronous communication
+ * The function qb_ipcc_sendv_recv() sends an iovector request and receives a response.
+ *
+ * @par Asynchronous requests from the client
+ * The function qb_ipcc_sendv() sends an iovector request.
+ * The function qb_ipcc_send() sends an message buffer request.
+ *
+ * @par Asynchronous events from the server
+ * The qb_ipcc_event_recv() function receives an out-of-band asyncronous message.
+ * The asynchronous messages are queued and can provide very high out-of-band performance.
+ * To determine when to call qb_ipcc_event_recv() the qb_ipcc_fd_get() call is
+ * used to obtain a file descriptor used in the poll() or select() system calls.
+ *
+ * @example ipcclient.c
+ * This is an example of how to use the client.
+ */
+
+typedef struct qb_ipcc_connection qb_ipcc_connection_t;
+
+/**
+ * Create a connection to an IPC service.
+ *
+ * @param name name of the service.
+ * @param max_msg_size biggest msg size.
+ * @return NULL (error: see errno) or a connection object.
+ *
+ * @note It is recommended to do a one time check on the
+ * max_msg_size value using qb_ipcc_verify_dgram_max_msg_size
+ * _BEFORE_ calling the connect function when IPC_SOCKET is in use.
+ * Some distributions while allow large message buffers to be
+ * set on the socket, but not actually honor them because of
+ * kernel state values.  The qb_ipcc_verify_dgram_max_msg_size
+ * function both sets the socket buffer size and verifies it by
+ * doing a send/recv.
+ */
+qb_ipcc_connection_t*
+qb_ipcc_connect(const char *name, size_t max_msg_size);
+
+/**
+ * Test kernel dgram socket buffers to verify the largest size up
+ * to the max_msg_size value a single msg can be. Rounds down to the
+ * nearest 1k.
+ *
+ * @param max_msg_size biggest msg size.
+ * @return -1 if max size can not be detected, positive value
+ *         representing the largest single msg up to max_msg_size
+ *         that can successfully be sent over a unix dgram socket.
+ */
+int32_t
+qb_ipcc_verify_dgram_max_msg_size(size_t max_msg_size);
+
+
+/**
+ * Disconnect an IPC connection.
+ *
+ * @param c connection instance
+ */
+void qb_ipcc_disconnect(qb_ipcc_connection_t* c);
+
+/**
+ * Get the file descriptor to poll.
+ *
+ * @param c connection instance
+ * @param fd (out) file descriptor to poll
+ */
+int32_t qb_ipcc_fd_get(qb_ipcc_connection_t* c, int32_t * fd);
+
+/**
+ * Set the maximum allowable flowcontrol value.
+ *
+ * @note the default is 1
+ *
+ * @param c connection instance
+ * @param max the max allowable flowcontrol value (1 or 2)
+ */
+int32_t qb_ipcc_fc_enable_max_set(qb_ipcc_connection_t * c, uint32_t max);
+
+/**
+ * Send a message.
+ *
+ * @param c connection instance
+ * @param msg_ptr pointer to a message to send
+ * @param msg_len the size of the message
+ * @return (size sent, -errno == error)
+ *
+ * @note the msg_ptr must include a qb_ipc_request_header at
+ * the top of the message. The server will read the size field
+ * to determine how much to recv.
+ */
+ssize_t qb_ipcc_send(qb_ipcc_connection_t* c, const void *msg_ptr,
+                     size_t msg_len);
+/**
+ * Send a message (iovec).
+ *
+ * @param c connection instance
+ * @param iov pointer to an iovec struct to send
+ * @param iov_len the number of iovecs used
+ * @return (size sent, -errno == error)
+ *
+ * @note the iov[0] must be a qb_ipc_request_header. The server will
+ * read the size field to determine how much to recv.
+ */
+ssize_t qb_ipcc_sendv(qb_ipcc_connection_t* c, const struct iovec* iov,
+	size_t iov_len);
+/**
+ * Receive a response.
+ *
+ * @param c connection instance
+ * @param msg_ptr pointer to a message buffer to receive into
+ * @param msg_len the size of the buffer
+ * @param ms_timeout max time to wait for a response
+ * @return (size recv'ed, -errno == error)
+ *
+ * @note that msg_ptr will include a qb_ipc_response_header at
+ * the top of the message.
+ */
+ssize_t qb_ipcc_recv(qb_ipcc_connection_t* c, void *msg_ptr,
+                     size_t msg_len, int32_t ms_timeout);
+
+/**
+ * This is a convenience function that simply sends and then recvs.
+ *
+ * @param c connection instance
+ * @param iov pointer to an iovec struct to send
+ * @param iov_len the number of iovecs used
+ * @param msg_ptr pointer to a message buffer to receive into
+ * @param msg_len the size of the buffer
+ * @param ms_timeout max time to wait for a response
+ *
+ * @note the iov[0] must include a qb_ipc_request_header at
+ * the top of the message. The server will read the size field
+ * to determine how much to recv.
+ * @note that msg_ptr will include a qb_ipc_response_header at
+ * the top of the message.
+ *
+ * @see qb_ipcc_sendv() qb_ipcc_recv()
+ */
+ssize_t qb_ipcc_sendv_recv(qb_ipcc_connection_t *c,
+			   const struct iovec *iov, uint32_t iov_len,
+			   void *msg_ptr, size_t msg_len,
+			   int32_t ms_timeout);
+
+/**
+ * Receive an event.
+ *
+ * @param c connection instance
+ * @param msg_ptr pointer to a message buffer to receive into
+ * @param msg_len the size of the buffer
+ * @param ms_timeout time in milli seconds to wait for a message
+ *        0 == no wait, negative == block, positive == wait X ms.
+ * @param ms_timeout max time to wait for a response
+ * @return size of the message or error (-errno)
+ *
+ * @note that msg_ptr will include a qb_ipc_response_header at
+ * the top of the message.
+ */
+ssize_t qb_ipcc_event_recv(qb_ipcc_connection_t* c, void *msg_ptr,
+			   size_t msg_len, int32_t ms_timeout);
+
+/**
+ * Associate a "user" pointer with this connection.
+ *
+ * @param context the point to associate with this connection.
+ * @param c connection instance
+ * @see qb_ipcc_context_get()
+ */
+void qb_ipcc_context_set(qb_ipcc_connection_t *c, void *context);
+
+/**
+ * Get the context (set previously)
+ *
+ * @param c connection instance
+ * @return the context
+ * @see qb_ipcc_context_set()
+ */
+void *qb_ipcc_context_get(qb_ipcc_connection_t *c);
+
+/**
+ * Is the connection connected?
+ *
+ * @param c connection instance
+ * @retval QB_TRUE when connected
+ * @retval QB_FALSE when not connected
+ */
+int32_t qb_ipcc_is_connected(qb_ipcc_connection_t *c);
+
+/**
+ * What is the actual buffer size used after the connection.
+ *
+ * @note The buffer size is guaranteed to be at least the size
+ * of the value given in qb_ipcc_connect, but it is possible
+ * the server will enforce a larger size depending on the
+ * implementation.  If the server side is known to enforce
+ * a buffer size, use this function after the client connection
+ * is established to retrieve the buffer size in use.  It is
+ * important for the client side to know the buffer size in use
+ * so the client can successfully retrieve large server events.
+ *
+ * @param c connection instance
+ * @retval connection size in bytes or -error code
+ */
+int32_t qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_IPCC_H_DEFINED */
diff --git a/include/qb/qbipcs.h b/include/qb/qbipcs.h
new file mode 100644
index 0000000..9ef2aa6
--- /dev/null
+++ b/include/qb/qbipcs.h
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>,
+ *         Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_IPCS_H_DEFINED
+#define QB_IPCS_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <qb/qbipc_common.h>
+#include <qb/qbhdb.h>
+#include <qb/qbloop.h>
+
+/**
+ * @file qbipcs.h
+ *
+ * Server IPC API.
+ *
+ * @example ipcserver.c
+ */
+
+enum qb_ipcs_rate_limit {
+	QB_IPCS_RATE_FAST,
+	QB_IPCS_RATE_NORMAL,
+	QB_IPCS_RATE_SLOW,
+	QB_IPCS_RATE_OFF,
+	QB_IPCS_RATE_OFF_2,
+};
+
+struct qb_ipcs_connection;
+typedef struct qb_ipcs_connection qb_ipcs_connection_t;
+
+struct qb_ipcs_service;
+typedef struct qb_ipcs_service qb_ipcs_service_t;
+
+struct qb_ipcs_stats {
+	uint32_t active_connections;
+	uint32_t closed_connections;
+};
+
+struct qb_ipcs_connection_stats {
+	int32_t client_pid;
+	uint64_t requests;
+	uint64_t responses;
+	uint64_t events;
+	uint64_t send_retries;
+	uint64_t recv_retries;
+	int32_t flow_control_state;
+	uint64_t flow_control_count;
+};
+
+struct qb_ipcs_connection_stats_2 {
+	int32_t client_pid;
+	uint64_t requests;
+	uint64_t responses;
+	uint64_t events;
+	uint64_t send_retries;
+	uint64_t recv_retries;
+	int32_t flow_control_state;
+	uint64_t flow_control_count;
+	uint32_t event_q_length;
+};
+
+typedef int32_t (*qb_ipcs_dispatch_fn_t) (int32_t fd, int32_t revents,
+					  void *data);
+
+typedef int32_t (*qb_ipcs_dispatch_add_fn)(enum qb_loop_priority p,
+					   int32_t fd,
+					   int32_t events,
+					   void *data,
+					   qb_ipcs_dispatch_fn_t fn);
+typedef int32_t (*qb_ipcs_dispatch_mod_fn)(enum qb_loop_priority p,
+					   int32_t fd,
+					   int32_t events,
+					   void *data,
+					   qb_ipcs_dispatch_fn_t fn);
+typedef int32_t (*qb_ipcs_dispatch_del_fn)(int32_t fd);
+
+typedef int32_t (*qb_ipcs_job_add_fn)(enum qb_loop_priority p,
+				      void *data,
+				      qb_loop_job_dispatch_fn dispatch_fn);
+
+struct qb_ipcs_poll_handlers {
+	qb_ipcs_job_add_fn job_add;
+	qb_ipcs_dispatch_add_fn dispatch_add;
+	qb_ipcs_dispatch_mod_fn dispatch_mod;
+	qb_ipcs_dispatch_del_fn dispatch_del;
+};
+
+/**
+ * This callback is to check whether you want to accept a new connection.
+ *
+ * The type of checks you should do are authentication, service availabilty
+ * or process resource constraints. 
+ * @return 0 to accept or -errno to indicate a failure (sent back to the client)
+ *
+ * @note If connection state data is allocated as a result of this callback
+ * being invoked, that data must be freed in the destroy callback. Just because
+ * the accept callback returns 0, that does not guarantee the
+ * create and closed callback functions will follow.
+ * @note you can call qb_ipcs_connection_auth_set() within this function.
+ */
+typedef int32_t (*qb_ipcs_connection_accept_fn) (qb_ipcs_connection_t *c,
+						 uid_t uid, gid_t gid);
+
+/**
+ * This is called after a new connection has been created.
+ *
+ * @note A client connection is not considered connected until
+ * this callback is invoked.
+ */
+typedef void (*qb_ipcs_connection_created_fn) (qb_ipcs_connection_t *c);
+
+/**
+ * This is called after a connection has been disconnected.
+ *
+ * @note This callback will only be invoked if the connection is
+ * successfully created.
+ * @note if you return anything but 0 this function will be
+ * repeativily called (until 0 is returned).
+ */
+typedef int32_t (*qb_ipcs_connection_closed_fn) (qb_ipcs_connection_t *c);
+
+/**
+ * This is called just before a connection is freed.
+ */
+typedef void (*qb_ipcs_connection_destroyed_fn) (qb_ipcs_connection_t *c);
+
+/**
+ * This is the message processing calback.
+ * It is called with the message data.
+ */
+typedef int32_t (*qb_ipcs_msg_process_fn) (qb_ipcs_connection_t *c,
+		void *data, size_t size);
+
+struct qb_ipcs_service_handlers {
+	qb_ipcs_connection_accept_fn connection_accept;
+	qb_ipcs_connection_created_fn connection_created;
+	qb_ipcs_msg_process_fn msg_process;
+	qb_ipcs_connection_closed_fn connection_closed;
+	qb_ipcs_connection_destroyed_fn connection_destroyed;
+};
+
+/**
+ * Create a new IPC server.
+ *
+ * @param name for clients to connect to.
+ * @param service_id an integer to associate with the service
+ * @param type transport type.
+ * @param handlers callbacks.
+ * @return the new service instance.
+ */
+qb_ipcs_service_t* qb_ipcs_create(const char *name,
+				  int32_t service_id,
+				  enum qb_ipc_type type,
+				  struct qb_ipcs_service_handlers *handlers);
+
+
+/**
+ * Increase the reference counter on the service object.
+ *
+ * @param s service instance
+ */
+void qb_ipcs_ref(qb_ipcs_service_t *s);
+
+/**
+ * Decrease the reference counter on the service object.
+ *
+ * @param s service instance
+ */
+void qb_ipcs_unref(qb_ipcs_service_t *s);
+
+/**
+ * Set your poll callbacks.
+ *
+ * @param s service instance
+ * @param handlers the handlers that you want ipcs to use.
+ */
+void qb_ipcs_poll_handlers_set(qb_ipcs_service_t* s,
+	struct qb_ipcs_poll_handlers *handlers);
+
+/**
+ * Associate a "user" pointer with this service.
+ *
+ * @param s service instance
+ * @param context the pointer to associate with this service.
+ * @see qb_ipcs_service_context_get()
+ */
+void qb_ipcs_service_context_set(qb_ipcs_service_t* s,
+	void *context);
+
+/**
+ * Get the context (set previously)
+ *
+ * @param s service instance
+ * @return the context
+ * @see qb_ipcs_service_context_set()
+ */
+void *qb_ipcs_service_context_get(qb_ipcs_service_t* s);
+
+/**
+ * run the new IPC server.
+ * @param s service instance
+ * @return 0 == ok; -errno to indicate a failure. Service is destroyed on failure.
+ */
+int32_t qb_ipcs_run(qb_ipcs_service_t* s);
+
+/**
+ * Destroy the IPC server.
+ *
+ * @param s service instance to destroy
+ */
+void qb_ipcs_destroy(qb_ipcs_service_t* s);
+
+/**
+ * Limit the incomming request rate.
+ * @param s service instance
+ * @param rl the new rate
+ */
+void qb_ipcs_request_rate_limit(qb_ipcs_service_t* s,
+			       	enum qb_ipcs_rate_limit rl);
+
+/**
+ * Send a response to a incomming request.
+ *
+ * @param c connection instance
+ * @param data the message to send
+ * @param size the size of the message
+ * @return size sent or -errno for errors
+ *
+ * @note the data must include a qb_ipc_response_header at
+ * the top of the message. The client will read the size field
+ * to determine how much to recv.
+ */
+ssize_t qb_ipcs_response_send(qb_ipcs_connection_t *c, const void *data,
+			      size_t size);
+
+/**
+ * Send a response to a incomming request.
+ *
+ * @param c connection instance
+ * @param iov the iovec struct that points to the message to send
+ * @param iov_len the number of iovecs.
+ * @return size sent or -errno for errors
+ *
+ * @note the iov[0] must be a qb_ipc_response_header. The client will
+ * read the size field to determine how much to recv.
+ *
+ * @note When send returns -EMSGSIZE, this means the msg is too
+ * large and will never succeed. To determine the max msg size
+ * a client can be sent, use qb_ipcs_connection_get_buffer_size()
+ */
+ssize_t qb_ipcs_response_sendv(qb_ipcs_connection_t *c,
+			       const struct iovec * iov, size_t iov_len);
+
+/**
+ * Send an asyncronous event message to the client.
+ *
+ * @param c connection instance
+ * @param data the message to send
+ * @param size the size of the message
+ * @return size sent or -errno for errors
+ *
+ * @note the data must include a qb_ipc_response_header at
+ * the top of the message. The client will read the size field
+ * to determine how much to recv.
+ *
+ * @note When send returns -EMSGSIZE, this means the msg is too
+ * large and will never succeed. To determine the max msg size
+ * a client can be sent, use qb_ipcs_connection_get_buffer_size()
+ */
+ssize_t qb_ipcs_event_send(qb_ipcs_connection_t *c, const void *data,
+			   size_t size);
+
+/**
+ * Send an asyncronous event message to the client.
+ *
+ * @param c connection instance
+ * @param iov the iovec struct that points to the message to send
+ * @param iov_len the number of iovecs.
+ * @return size sent or -errno for errors
+ *
+ * @note the iov[0] must be a qb_ipc_response_header. The client will
+ * read the size field to determine how much to recv.
+ *
+ * @note When send returns -EMSGSIZE, this means the msg is too
+ * large and will never succeed. To determine the max msg size
+ * a client can be sent, use qb_ipcs_connection_get_buffer_size()
+ */
+ssize_t qb_ipcs_event_sendv(qb_ipcs_connection_t *c, const struct iovec * iov,
+			    size_t iov_len);
+
+/**
+ * Increment the connection's reference counter.
+ *
+ * @param c connection instance
+ */
+void qb_ipcs_connection_ref(qb_ipcs_connection_t *c);
+
+/**
+ * Decrement the connection's reference counter.
+ *
+ * @param c connection instance
+ */
+void qb_ipcs_connection_unref(qb_ipcs_connection_t *c);
+
+/**
+ * Disconnect from this client.
+ *
+ * @param c connection instance
+ */
+void qb_ipcs_disconnect(qb_ipcs_connection_t *c);
+
+/**
+ * Get the service id related to this connection's service.
+ * (as passed into qb_ipcs_create()
+ *
+ * @return service id.
+ */
+int32_t qb_ipcs_service_id_get(qb_ipcs_connection_t *c);
+
+/**
+ * Associate a "user" pointer with this connection.
+ *
+ * @param context the point to associate with this connection.
+ * @param c connection instance
+ * @see qb_ipcs_context_get()
+ */
+void qb_ipcs_context_set(qb_ipcs_connection_t *c, void *context);
+
+/**
+ * Get the context (set previously)
+ *
+ * @param c connection instance
+ * @return the context
+ * @see qb_ipcs_context_set()
+ */
+void *qb_ipcs_context_get(qb_ipcs_connection_t *c);
+
+/**
+ * Get the context previously set on the service backing this connection
+ *
+ * @param c connection instance
+ * @return the context
+ * @see qb_ipcs_service_context_set
+ */
+void *qb_ipcs_connection_service_context_get(qb_ipcs_connection_t *c);
+
+/**
+ * Get the connection statistics.
+ *
+ * @deprecated from v0.13.0 onwards, use qb_ipcs_connection_stats_get_2
+ * @param stats (out) the statistics structure
+ * @param clear_after_read clear stats after copying them into stats
+ * @param c connection instance
+ * @return 0 == ok; -errno to indicate a failure
+ */
+int32_t qb_ipcs_connection_stats_get(qb_ipcs_connection_t *c,
+				     struct qb_ipcs_connection_stats* stats,
+				     int32_t clear_after_read);
+/**
+ * Get (and allocate) the connection statistics.
+ *
+ * @param clear_after_read clear stats after copying them into stats
+ * @param c connection instance
+ * @retval NULL if no memory or invalid connection
+ * @retval allocated statistics structure (user must free it).
+ */
+struct qb_ipcs_connection_stats_2*
+qb_ipcs_connection_stats_get_2(qb_ipcs_connection_t *c,
+			       int32_t clear_after_read);
+
+/**
+ * Get the service statistics.
+ *
+ * @param stats (out) the statistics structure
+ * @param clear_after_read clear stats after copying them into stats
+ * @param pt service instance
+ * @return 0 == ok; -errno to indicate a failure
+ */
+int32_t qb_ipcs_stats_get(qb_ipcs_service_t* pt,
+			  struct qb_ipcs_stats* stats,
+			  int32_t clear_after_read);
+
+/**
+ * Get the first connection.
+ *
+ * @note call qb_ipcs_connection_unref() after using the connection.
+ *
+ * @param pt service instance
+ * @return first connection
+ */
+qb_ipcs_connection_t * qb_ipcs_connection_first_get(qb_ipcs_service_t* pt);
+
+/**
+ * Get the next connection.
+ *
+ * @note call qb_ipcs_connection_unref() after using the connection.
+ *
+ * @param pt service instance
+ * @param current current connection
+ * @return next connection
+ */
+qb_ipcs_connection_t * qb_ipcs_connection_next_get(qb_ipcs_service_t* pt,
+						   qb_ipcs_connection_t *current);
+
+/**
+ * Set the permissions on and shared memory files so that both processes can
+ * read and write to them.
+ *
+ * @param conn connection instance
+ * @param uid the user id to set.
+ * @param gid the group id to set.
+ * @param mode the mode to set.
+ *
+ * @see chmod() chown()
+ * @note this must be called within the qb_ipcs_connection_accept_fn()
+ * callback.
+ */
+void qb_ipcs_connection_auth_set(qb_ipcs_connection_t *conn, uid_t uid,
+				 gid_t gid, mode_t mode);
+
+/**
+ * Retrieve the connection ipc buffer size. This reflects the
+ * largest size msg that can be sent or received.
+ *
+ * @param conn connection instance
+ * @return msg size in bytes, negative value on error.
+ */
+int32_t qb_ipcs_connection_get_buffer_size(qb_ipcs_connection_t *conn);
+
+/**
+ * Enforce the max buffer size clients must use from the server side.
+ *
+ * @note Setting this will force client connections to use at least
+ * max_buf_size bytes as their buffer size.  If this value is not set
+ * on the server, the clients enforce their own buffer sizes.
+ *
+ * @param s ipc server instance
+ * @param max_buf_size represented in bytes
+ */
+void qb_ipcs_enforce_buffer_size(qb_ipcs_service_t *s, uint32_t max_buf_size);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_IPCS_H_DEFINED */
diff --git a/include/qb/qblist.h b/include/qb/qblist.h
new file mode 100644
index 0000000..f8ab0d2
--- /dev/null
+++ b/include/qb/qblist.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2006-2010, 2009 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_LIST_H_DEFINED
+#define QB_LIST_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <stdint.h>
+#include <qb/qbdefs.h>
+
+/**
+ * @file qblist.h
+ * This is a kernel style list implementation.
+ *
+ * @author Steven Dake <sdake at redhat.com>
+ */
+
+struct qb_list_head {
+	struct qb_list_head *next;
+	struct qb_list_head *prev;
+};
+
+/**
+ * @def QB_LIST_DECLARE()
+ * Declare and initialize a list head.
+ */
+#define QB_LIST_DECLARE(name) \
+    struct qb_list_head name = { &(name), &(name) }
+
+#define QB_INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/**
+ * Initialize the list entry.
+ *
+ * Points next and prev pointers to head.
+ * @param head pointer to the list head
+ */
+static inline void qb_list_init(struct qb_list_head *head)
+{
+	head->next = head;
+	head->prev = head;
+}
+
+/**
+ * Add this element to the list.
+ *
+ * @param element the new element to insert.
+ * @param head pointer to the list head
+ */
+static inline void qb_list_add(struct qb_list_head *element,
+			       struct qb_list_head *head)
+{
+	head->next->prev = element;
+	element->next = head->next;
+	element->prev = head;
+	head->next = element;
+}
+
+/**
+ * Add to the list (but at the end of the list).
+ *
+ * @param element pointer to the element to add
+ * @param head pointer to the list head
+ * @see qb_list_add()
+ */
+static inline void qb_list_add_tail(struct qb_list_head *element,
+				    struct qb_list_head *head)
+{
+	head->prev->next = element;
+	element->next = head;
+	element->prev = head->prev;
+	head->prev = element;
+}
+
+/**
+ * Delete an entry from the list.
+ *
+ * @param _remove the list item to remove
+ */
+static inline void qb_list_del(struct qb_list_head *_remove)
+{
+	_remove->next->prev = _remove->prev;
+	_remove->prev->next = _remove->next;
+}
+
+/**
+ * Replace old entry by new one
+ * @param old: the element to be replaced
+ * @param new: the new element to insert
+ */
+static inline void qb_list_replace(struct qb_list_head *old,
+		struct qb_list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+/**
+ * Tests whether list is the last entry in list head
+ * @param list: the entry to test
+ * @param head: the head of the list
+ * @return boolean true/false
+ */
+static inline int qb_list_is_last(const struct qb_list_head *list,
+		const struct qb_list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * A quick test to see if the list is empty (pointing to it's self).
+ * @param head pointer to the list head
+ * @return boolean true/false
+ */
+static inline int32_t qb_list_empty(const struct qb_list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * Join two lists.
+ * @param list the new list to add.
+ * @param head the place to add it in the first list.
+ *
+ * @note The "list" is reinitialised
+ */
+static inline void qb_list_splice(struct qb_list_head *list,
+				  struct qb_list_head *head)
+{
+	struct qb_list_head *first = list->next;
+	struct qb_list_head *last = list->prev;
+	struct qb_list_head *at = head->next;
+
+	if (!qb_list_empty(list)) {
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * Join two lists, each list being a queue
+ * @param list: the new list to add.
+ * @param head: the place to add it in the first list.
+ */
+static inline void qb_list_splice_tail(struct qb_list_head *list,
+				struct qb_list_head *head)
+{
+	struct qb_list_head *first = list->next;
+	struct qb_list_head *last = list->prev;
+	struct qb_list_head *at = head;
+
+	if (!qb_list_empty(list)) {
+		first->prev = head->prev;
+		head->prev->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * Get the struct for this entry
+ * @param ptr:	the &struct list_head pointer.
+ * @param type:	the type of the struct this is embedded in.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_entry(ptr,type,member)\
+	((type *)((char *)(ptr)-(char*)(&((type *)0)->member)))
+
+/**
+ * Get the first element from a list
+ * @param ptr:	the &struct list_head pointer.
+ * @param type:	the type of the struct this is embedded in.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_first_entry(ptr, type, member) \
+	qb_list_entry((ptr)->next, type, member)
+
+/**
+ * Iterate over a list
+ * @param pos:	the &struct list_head to use as a loop counter.
+ * @param head:	the head for your list.
+ */
+#define qb_list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * Iterate over a list backwards
+ * @param pos:	the &struct list_head to use as a loop counter.
+ * @param head:	the head for your list.
+ */
+#define qb_list_for_each_reverse(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * Iterate over a list safe against removal of list entry
+ * @param pos:	the &struct list_head to use as a loop counter.
+ * @param n:		another &struct list_head to use as temporary storage
+ * @param head:	the head for your list.
+ */
+#define qb_list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * Iterate over list of given type
+ * @param pos:	the type * to use as a loop counter.
+ * @param head:	the head for your list.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_for_each_entry(pos, head, member)			\
+	for (pos = qb_list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = qb_list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * Iterate backwards over list of given type.
+ * @param pos:	the type to use as a loop counter.
+ * @param head:	the head for your list.
+ * @param member: the name of the list_struct within the struct.
+ */
+#define qb_list_for_each_entry_reverse(pos, head, member)		\
+	for (pos = qb_list_entry((head)->prev, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = qb_list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * Iterate over list of given type safe against removal of list entry
+ * @param pos:		the type * to use as a loop cursor.
+ * @param n:		another type * to use as temporary storage
+ * @param head:		the head for your list.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = qb_list_entry((head)->next, typeof(*pos), member),		\
+		n = qb_list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 						\
+	     pos = n, n = qb_list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * Iterate backwards over list safe against removal
+ * @param pos:		the type * to use as a loop cursor.
+ * @param n:		another type * to use as temporary storage
+ * @param head:		the head for your list.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = qb_list_entry((head)->prev, typeof(*pos), member),		\
+		n = qb_list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 						\
+	     pos = n, n = qb_list_entry(n->member.prev, typeof(*n), member))
+
+/**
+ * Iterate over list of given type from the current point
+ * @param pos:		the type * to use as a loop cursor.
+ * @param head:		the head for your list.
+ * @param member:	the name of the list_struct within the struct.
+ */
+#define qb_list_for_each_entry_from(pos, head, member)				\
+	for (; &pos->member != (head);						\
+	     pos = qb_list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * Count the number of items in the list.
+ * @param head:	the head for your list.
+ * @return length of the list.
+ */
+static inline int32_t qb_list_length(struct qb_list_head *head)
+{
+	struct qb_list_head *item;
+	int32_t length = 0;
+
+	qb_list_for_each(item, head)
+		length++;
+
+	return length;
+}
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+
+#endif /* QB_LIST_H_DEFINED */
diff --git a/include/qb/qblog.h b/include/qb/qblog.h
new file mode 100644
index 0000000..35fcc58
--- /dev/null
+++ b/include/qb/qblog.h
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_LOG_H_DEFINED
+#define QB_LOG_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <qb/qbutil.h>
+#include <qb/qbconfig.h>
+
+#ifdef S_SPLINT_S
+#undef QB_HAVE_ATTRIBUTE_SECTION
+#endif /* S_SPLINT_S */
+
+/**
+ * @file qblog.h
+ * The logging API provides four main parts (basics, filtering, threading & blackbox).
+ *
+ * The idea behind this logging system is not to be prescriptive but to provide a
+ * set of tools to help the developer achieve what they want quickly and easily.
+ *
+ * @par Basic logging API.
+ * Call qb_log() to generate a log message. Then to write the message
+ * somewhere meaningful call qb_log_ctl() to configure the targets.
+ *
+ * Simplist possible use:
+ * @code
+ * main() {
+ *	qb_log_init("simple-log", LOG_DAEMON, LOG_INFO);
+ * 	// ...
+ *	qb_log(LOG_WARNING, "watch out");
+ * 	// ...
+ *	qb_log_fini();
+ * }
+ * @endcode
+ *
+ * @par Configuring log targets.
+ * A log target can by syslog, stderr, the blackbox or a text file.
+ * By default only syslog is enabled.
+ *
+ * To enable a target do the following
+ * @code
+ *	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+ * @endcode
+ *
+ * syslog, stderr and the blackbox are static (they don't need
+ * to be created, just enabled or disabled. However you can open multiple
+ * logfiles (32 - QB_LOG_BLACKBOX). To do this use the following code.
+ * @code
+ *	mytarget = qb_log_file_open("/var/log/mylogfile");
+ *	qb_log_ctl(mytarget, QB_LOG_CONF_ENABLED, QB_TRUE);
+ * @endcode
+ *
+ * Once your targets are enabled/opened you can configure them as follows:
+ * Configure the size of blackbox
+ * @code
+ *	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10);
+ * @endcode
+ *
+ * Make logging to file threaded:
+ * @code
+ *	qb_log_ctl(mytarget, QB_LOG_CONF_THREADED, QB_TRUE);
+ * @endcode
+ *
+ * To workaround your syslog daemon filtering all messages > LOG_INFO
+ * @code
+ *	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP,
+ *		   LOG_INFO - LOG_DEBUG);
+ * @endcode
+ *
+ * To ensure all logs to file targets are fsync'ed (default QB_FALSE)
+ * @code
+ *	qb_log_ctl(mytarget, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
+ * @endcode
+ *
+ *
+ * @par Filtering messages.
+ * To have more power over what log messages go to which target you can apply
+ * filters to the targets. What happens is the desired callsites have the
+ * correct bit set. Then when the log message is generated it gets sent to the
+ * targets based on which bit is set in the callsite's "target" bitmap.
+ * Messages can be filtered based on the:
+ * -# filename + priority
+ * -# function name + priority
+ * -# format string + priority
+ *
+ * So to make all logs from evil_fnunction() go to stderr do the following:
+ * @code
+ *	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+ *			  QB_LOG_FILTER_FUNCTION, "evil_fnunction", LOG_TRACE);
+ * @endcode
+ *
+ * So to make all logs from totem* (with  a priority <= LOG_INFO) go to stderr do the following:
+ * @code
+ *	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+ *			  QB_LOG_FILTER_FILE, "totem", LOG_INFO);
+ * @endcode
+ *
+ * So to make all logs with the substring "ringbuffer" go to stderr do the following:
+ * @code
+ *	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+ *			  QB_LOG_FILTER_FORMAT, "ringbuffer", LOG_TRACE);
+ * @endcode
+ *
+ * @par Thread safe non-blocking logging.
+ * Logging is only thread safe when threaded logging is in use. If you plan
+ * on logging from multiple threads, you must initialize libqb's logger thread
+ * and use qg_log_filter_ctl to set the QB_LOG_CONF_THREADED flag on all the
+ * logging targets in use.
+ *
+ * To achieve non-blocking logging you can use threaded logging as well
+ * So any calls to write() or syslog() will not hold up your program.
+ *
+ * Threaded logging use:
+ * @code
+ * main() {
+ *	qb_log_init("simple-log", LOG_DAEMON, LOG_INFO);
+ *	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, QB_TRUE);
+ * 	// ...
+ * 	daemonize();
+ * 	// call this after you fork()
+ * 	qb_log_thread_start();
+ * 	// ...
+ *	qb_log(LOG_WARNING, "watch out");
+ * 	// ...
+ *	qb_log_fini();
+ * }
+ * @endcode
+ *
+ * @par A blackbox for in-field diagnosis.
+ * This stores log messages in a ringbuffer so they can be written to
+ * file if the program crashes (you will need to catch SIGSEGV). These
+ * can then be easily printed out later.
+ *
+ * @note the blackbox is not enabled by default.
+ *
+ * Blackbox usage:
+ * @code
+ *
+ * static void sigsegv_handler(int sig)
+ * {
+ * 	(void)signal (SIGSEGV, SIG_DFL);
+ * 	qb_log_blackbox_write_to_file("simple-log.fdata");
+ * 	qb_log_fini();
+ * 	raise(SIGSEGV);
+ * }
+ *
+ * main() {
+ *
+ *	signal(SIGSEGV, sigsegv_handler);
+ *
+ *	qb_log_init("simple-log", LOG_DAEMON, LOG_INFO);
+ *	qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
+ *			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+ *	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024*10);
+ *	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+ * 	// ...
+ *	qb_log(LOG_WARNING, "watch out");
+ * 	// ...
+ *	qb_log_fini();
+ * }
+ * @endcode
+ *
+ * @par Tagging messages.
+ * You can tag messages using the second argument to qb_logt() or
+ * by using qb_log_filter_ctl().
+ * This can be used to add feature or sub-system information to the logs.
+ *
+ * @code
+ * const char* my_tags_stringify(uint32_t tags) {
+ * 	if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT) {
+ * 		return "libqb";
+ * 	} else if (tags == 3) {
+ * 		return "three";
+ * 	} else {
+ * 		return "MAIN";
+ * 	}
+ * }
+ * main() {
+ * 	// ...
+ * 	qb_log_tags_stringify_fn_set(my_tags_stringify);
+ * 	qb_log_format_set(QB_LOG_STDERR, "[%5g] %p %b");
+ * 	// ...
+ * 	qb_logt(LOG_INFO, 3, "hello");
+ * 	qb_logt(LOG_INFO, 0, "hello");
+ * }
+ * @endcode
+ * The code above will produce:
+ * @code
+ * [libqb] some message
+ * [three] info hello
+ * [MAIN ] info hello
+ * @endcode
+ *
+ * @example simplelog.c
+ */
+
+#undef LOG_TRACE
+#define LOG_TRACE    (LOG_DEBUG + 1)
+
+#define QB_LOG_MAX_LEN 512
+#define QB_LOG_STRERROR_MAX_LEN 128
+
+typedef const char *(*qb_log_tags_stringify_fn)(uint32_t tags);
+
+/**
+ * An instance of this structure is created in a special
+ * ELF section at every dynamic debug callsite.  At runtime,
+ * the special section is treated as an array of these.
+ */
+struct qb_log_callsite {
+	const char *function;
+	const char *filename;
+	const char *format;
+	uint8_t priority;
+	uint32_t lineno;
+	uint32_t targets;
+	uint32_t tags;
+} __attribute__((aligned(8)));
+
+typedef void (*qb_log_filter_fn)(struct qb_log_callsite * cs);
+
+/* will be assigned by ld linker magic */
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
+extern struct qb_log_callsite __start___verbose[];
+extern struct qb_log_callsite __stop___verbose[];
+
+#define QB_LOG_INIT_DATA(name)						\
+    void name(void);							\
+    void name(void) { if (__start___verbose != __stop___verbose) {assert(1);} }	\
+    void __attribute__ ((constructor)) name(void);
+#else
+#define QB_LOG_INIT_DATA(name)
+#endif
+
+/**
+ * Internal function: use qb_log() or qb_logt()
+ */
+void qb_log_real_(struct qb_log_callsite *cs, ...);
+void qb_log_real_va_(struct qb_log_callsite *cs, va_list ap);
+
+#define QB_LOG_TAG_LIBQB_MSG_BIT 31
+#define QB_LOG_TAG_LIBQB_MSG (1 << QB_LOG_TAG_LIBQB_MSG_BIT)
+
+/**
+ * This function is to import logs from other code (like libraries)
+ * that provide a callback with their logs.
+ *
+ * @note the performance of this will not impress you, as
+ * the filtering is done on each log message, not
+ * before hand. So try doing basic pre-filtering.
+ *
+ * @param function originating function name
+ * @param filename originating filename
+ * @param format format string
+ * @param priority this takes syslog priorities.
+ * @param lineno file line number
+ * @param tags this is a uint32_t that you can use with
+ *             qb_log_tags_stringify_fn_set() to "tag" a log message
+ *             with a feature or sub-system then you can use "%g"
+ *             in the format specifer to print it out.
+ */
+void qb_log_from_external_source(const char *function,
+				 const char *filename,
+				 const char *format,
+				 uint8_t priority,
+				 uint32_t lineno,
+				 uint32_t tags,
+				 ...);
+
+/**
+ * Get or create a callsite at the give position.
+ *
+ * The result can then be passed into qb_log_real_()
+ *
+ * @param function originating function name
+ * @param filename originating filename
+ * @param format format string
+ * @param priority this takes syslog priorities.
+ * @param lineno file line number
+ * @param tags the tag
+ */
+struct qb_log_callsite* qb_log_callsite_get(const char *function,
+					    const char *filename,
+					    const char *format,
+					    uint8_t priority,
+					    uint32_t lineno,
+					    uint32_t tags);
+
+void qb_log_from_external_source_va(const char *function,
+				    const char *filename,
+				    const char *format,
+				    uint8_t priority,
+				    uint32_t lineno,
+				    uint32_t tags,
+				    va_list ap);
+
+/**
+ * This is the function to generate a log message if you want to
+ * manually add tags.
+ *
+ * @param priority this takes syslog priorities.
+ * @param tags this is a uint32_t that you can use with
+ *             qb_log_tags_stringify_fn_set() to "tag" a log message
+ *             with a feature or sub-system then you can use "%g"
+ *             in the format specifer to print it out.
+ * @param fmt usual printf style format specifiers
+ * @param args usual printf style args
+ */
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
+#define qb_logt(priority, tags, fmt, args...) do {			\
+	static struct qb_log_callsite descriptor			\
+	__attribute__((section("__verbose"), aligned(8))) =		\
+	{ __func__, __FILE__, fmt, priority, __LINE__, 0, tags };	\
+	qb_log_real_(&descriptor, ##args);				\
+    } while(0)
+#else
+#define qb_logt(priority, tags, fmt, args...) do {	\
+	struct qb_log_callsite* descriptor_pt =		\
+	qb_log_callsite_get(__func__, __FILE__, fmt,	\
+			    priority, __LINE__, tags);	\
+	qb_log_real_(descriptor_pt, ##args);		\
+    } while(0)
+#endif /* QB_HAVE_ATTRIBUTE_SECTION */
+
+
+/**
+ * This is the main function to generate a log message.
+ *
+ * @param priority this takes syslog priorities.
+ * @param fmt usual printf style format specifiers
+ * @param args usual printf style args
+ */
+#define qb_log(priority, fmt, args...) qb_logt(priority, 0, fmt, ##args)
+
+/**
+ * This is similar to perror except it goes into the logging system.
+ *
+ * @param priority this takes syslog priorities.
+ * @param fmt usual printf style format specifiers
+ * @param args usual printf style args
+ */
+#ifndef S_SPLINT_S
+#define qb_perror(priority, fmt, args...) do {				\
+	char _perr_buf_[QB_LOG_STRERROR_MAX_LEN];			\
+	const char *_perr_str_ = qb_strerror_r(errno, _perr_buf_, sizeof(_perr_buf_));	\
+	qb_logt(priority, 0, fmt ": %s (%d)", ##args, _perr_str_, errno);		\
+    } while(0)
+#else
+#define qb_perror
+#endif
+
+#define qb_enter() qb_log(LOG_TRACE, "ENTERING %s()", __func__)
+#define qb_leave() qb_log(LOG_TRACE, "LEAVING %s()", __func__)
+
+#define QB_LOG_SYSLOG 0
+#define QB_LOG_STDERR 1
+#define QB_LOG_BLACKBOX 2
+#define QB_LOG_STDOUT 3
+
+#define QB_LOG_TARGET_MAX 32
+
+enum qb_log_target_state {
+	QB_LOG_STATE_UNUSED = 1,
+	QB_LOG_STATE_DISABLED = 2,
+	QB_LOG_STATE_ENABLED = 3,
+};
+
+enum qb_log_conf {
+	QB_LOG_CONF_ENABLED,
+	QB_LOG_CONF_FACILITY,
+	QB_LOG_CONF_DEBUG,
+	QB_LOG_CONF_SIZE,
+	QB_LOG_CONF_THREADED,
+	QB_LOG_CONF_PRIORITY_BUMP,
+	QB_LOG_CONF_STATE_GET,
+	QB_LOG_CONF_FILE_SYNC,
+};
+
+enum qb_log_filter_type {
+	QB_LOG_FILTER_FILE,
+	QB_LOG_FILTER_FUNCTION,
+	QB_LOG_FILTER_FORMAT,
+	QB_LOG_FILTER_FILE_REGEX,
+	QB_LOG_FILTER_FUNCTION_REGEX,
+	QB_LOG_FILTER_FORMAT_REGEX,
+};
+
+enum qb_log_filter_conf {
+	QB_LOG_FILTER_ADD,
+	QB_LOG_FILTER_REMOVE,
+	QB_LOG_FILTER_CLEAR_ALL,
+	QB_LOG_TAG_SET,
+	QB_LOG_TAG_CLEAR,
+	QB_LOG_TAG_CLEAR_ALL,
+};
+
+typedef void (*qb_log_logger_fn)(int32_t t,
+				 struct qb_log_callsite *cs,
+				 time_t timestamp,
+				 const char *msg);
+typedef void (*qb_log_vlogger_fn)(int32_t t,
+				 struct qb_log_callsite *cs,
+				 time_t timestamp,
+				 va_list ap);
+
+typedef void (*qb_log_close_fn)(int32_t t);
+typedef void (*qb_log_reload_fn)(int32_t t);
+
+/**
+ * Init the logging system.
+ *
+ * @param name will be passed into openlog()
+ * @param facility default for all new targets.
+ * @param priority a basic filter with this priority will be added.
+ */
+void qb_log_init(const char *name,
+		 int32_t facility,
+		 uint8_t priority);
+
+/**
+ * Logging system finalization function.
+ *
+ * It releases any shared memory.
+ * Stops the logging thread if running.
+ * Flushes the last message to their destinations.
+ */
+void qb_log_fini(void);
+
+/**
+ * If you are using dynamically loadable modules via dlopen() and
+ * you load them after qb_log_init() then after you load the module
+ * you will need to do the following to get the filters to work
+ * in that module.
+ * @code
+ * 	_start = dlsym (dl_handle, "__start___verbose");
+ *	_stop = dlsym (dl_handle, "__stop___verbose");
+ *	qb_log_callsites_register(_start, _stop);
+ * @endcode
+ */
+int32_t qb_log_callsites_register(struct qb_log_callsite *_start, struct qb_log_callsite *_stop);
+
+/**
+ * Dump the callsite info to stdout.
+ */
+void qb_log_callsites_dump(void);
+
+/**
+ * Main logging control function.
+ *
+ * @param target QB_LOG_SYSLOG, QB_LOG_STDERR or result from qb_log_file_open()
+ * @param conf_type what to configure
+ * @param arg the new value
+ * @see qb_log_conf
+ *
+ * @retval -errno on error
+ * @retval 0 on success
+ * @retval qb_log_target_state for QB_LOG_CONF_STATE_GET
+ */
+int32_t qb_log_ctl(int32_t target, enum qb_log_conf conf_type, int32_t arg);
+
+/**
+ * This allows you modify the 'tags' and 'targets' callsite fields at runtime.
+ */
+int32_t qb_log_filter_ctl(int32_t value, enum qb_log_filter_conf c,
+			  enum qb_log_filter_type type, const char * text,
+			  uint8_t low_priority);
+
+
+/**
+ * This extends qb_log_filter_ctl() by been able to provide a high_priority.
+ */
+int32_t qb_log_filter_ctl2(int32_t value, enum qb_log_filter_conf c,
+			  enum qb_log_filter_type type, const char * text,
+			  uint8_t high_priority, uint8_t low_priority);
+
+
+/**
+ * Instead of using the qb_log_filter_ctl() functions you
+ * can apply the filters manually by defining a callback
+ * and setting the targets field using qb_bit_set() and
+ * qb_bit_clear() like the following below.
+ * @code
+ * static void
+ * m_filter(struct qb_log_callsite *cs)
+ * {
+ * 	if ((cs->priority >= LOG_ALERT &&
+ * 	     cs->priority <= LOG_DEBUG) &&
+ * 	     strcmp(cs->filename, "my_c_file.c") == 0) {
+ * 		qb_bit_set(cs->targets, QB_LOG_SYSLOG);
+ * 	} else {
+ * 		qb_bit_clear(cs->targets, QB_LOG_SYSLOG);
+ * 	}
+ * }
+ * @endcode
+ */
+int32_t qb_log_filter_fn_set(qb_log_filter_fn fn);
+
+/**
+ * Set the callback to map the 'tags' bit map to a string.
+ */
+void qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn);
+
+/**
+ * Set the format specifiers.
+ *
+ * %n FUNCTION NAME
+ * %f FILENAME
+ * %l FILELINE
+ * %p PRIORITY
+ * %t TIMESTAMP
+ * %b BUFFER
+ * %g TAGS
+ * %N name (passed into qb_log_init)
+ * %P PID
+ * %H hostname
+ *
+ * any number between % and character specify field length to pad or chop
+ */
+void qb_log_format_set(int32_t t, const char* format);
+
+/**
+ * Open a log file.
+ *
+ * @retval -errno on error
+ * @retval 3 to 31 (to be passed into other qb_log_* functions)
+ */
+int32_t qb_log_file_open(const char *filename);
+
+/**
+ * Close a log file and release is resources.
+ */
+void qb_log_file_close(int32_t t);
+
+/**
+ * When using threaded logging set the pthread policy and priority.
+ *
+ * @retval -errno on error
+ * @retval 0 success
+ */
+int32_t qb_log_thread_priority_set(int32_t policy, int32_t priority);
+
+/**
+ * Start the logging pthread.
+ */
+int32_t qb_log_thread_start(void);
+
+/**
+ * Write the blackbox to file.
+ */
+ssize_t qb_log_blackbox_write_to_file(const char *filename);
+
+/**
+ * Read the blackbox for file and print it out.
+ */
+void qb_log_blackbox_print_from_file(const char* filename);
+
+/**
+ * Open a custom log target.
+ *
+ * @retval -errno on error
+ * @retval 3 to 31 (to be passed into other qb_log_* functions)
+ */
+int32_t qb_log_custom_open(qb_log_logger_fn log_fn,
+			   qb_log_close_fn close_fn,
+			   qb_log_reload_fn reload_fn,
+			   void *user_data);
+
+/**
+ * Close a custom log target and release is resources.
+ */
+void qb_log_custom_close(int32_t t);
+
+/**
+ * Retrieve the user data set by either qb_log_custom_open or
+ * qb_log_target_user_data_set.
+ */
+void *qb_log_target_user_data_get(int32_t t);
+
+/**
+ * Associate user data with this log target
+ * @note only use this with custom targets
+ */
+int32_t qb_log_target_user_data_set(int32_t t, void *user_data);
+
+/**
+ * format the callsite and timestamp info according to the format
+ * set using qb_log_format_set()
+ * It is intended to be used from your custom logger function.
+ */
+void qb_log_target_format(int32_t target,
+			  struct qb_log_callsite *cs,
+			  time_t timestamp,
+			  const char* formatted_message,
+			  char *output_buffer);
+
+/**
+ * Convert string "auth" to equivalent number "LOG_AUTH" etc.
+ */
+int32_t qb_log_facility2int(const char *fname);
+
+/**
+ * Convert number "LOG_AUTH" to equivalent string "auth" etc.
+ */
+const char * qb_log_facility2str(int32_t fnum);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+#endif /* QB_LOG_H_DEFINED */
diff --git a/include/qb/qbloop.h b/include/qb/qbloop.h
new file mode 100644
index 0000000..9f09c7e
--- /dev/null
+++ b/include/qb/qbloop.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_LOOP_H_DEFINED
+#define QB_LOOP_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <signal.h>
+#include <stdint.h>
+
+/**
+ * @file qbloop.h
+ *
+ * Main loop manages timers, jobs and polling sockets.
+ *
+ * @example tcpserver.c
+ */
+
+
+/**
+ * Priorites for jobs, timers & poll
+ */
+enum qb_loop_priority {
+	QB_LOOP_LOW = 0,
+	QB_LOOP_MED = 1,
+	QB_LOOP_HIGH = 2,
+};
+
+/**
+ * An opaque data type representing the main loop.
+ */
+typedef struct qb_loop qb_loop_t;
+
+typedef uint64_t qb_loop_timer_handle;
+
+typedef void *qb_loop_signal_handle;
+
+typedef int32_t (*qb_loop_poll_dispatch_fn) (int32_t fd, int32_t revents, void *data);
+typedef void (*qb_loop_job_dispatch_fn)(void *data);
+typedef void (*qb_loop_timer_dispatch_fn)(void *data);
+typedef int32_t (*qb_loop_signal_dispatch_fn)(int32_t rsignal, void *data);
+
+typedef void (*qb_loop_poll_low_fds_event_fn) (int32_t not_enough, int32_t fds_available);
+
+/**
+ * Create a new main loop.
+ * 
+ * @return loop instance.
+ */
+qb_loop_t * qb_loop_create(void);
+
+/**
+ *
+ */
+void qb_loop_destroy(struct qb_loop * l);
+
+/**
+ * Stop the main loop.
+ * @param l pointer to the loop instance
+ */
+void qb_loop_stop(qb_loop_t *l);
+
+/**
+ * Run the main loop.
+ *
+ * @param l pointer to the loop instance
+ */
+void qb_loop_run(qb_loop_t *l);
+
+
+/**
+ * Add a job to the mainloop.
+ *
+ * This is run in the next cycle of the loop.
+ * @note it is a one-shot job.
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_job_add(qb_loop_t *l,
+			enum qb_loop_priority p,
+			void *data,
+			qb_loop_job_dispatch_fn dispatch_fn);
+
+
+/**
+ * Delete a job from the mainloop.
+ *
+ * This will try to delete the job if it hasn't run yet.
+ *
+ * @note this will remove the first job that matches the
+ * paramaters (priority, data, dispatch_fn).
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_job_del(struct qb_loop *l,
+			enum qb_loop_priority p,
+			void *data,
+			qb_loop_job_dispatch_fn dispatch_fn);
+
+/**
+ * Add a timer to the mainloop.
+ * @note it is a one-shot job.
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param nsec_duration nano-secs in the future to run the dispatch.
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @param timer_handle_out handle to delete the timer if needed.
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_timer_add(qb_loop_t *l,
+			  enum qb_loop_priority p,
+			  uint64_t nsec_duration,
+			  void *data,
+			  qb_loop_timer_dispatch_fn dispatch_fn,
+			  qb_loop_timer_handle * timer_handle_out);
+
+/**
+ * Delete a timer that is still outstanding.
+ *
+ * @param l pointer to the loop instance
+ * @param th handle to delete the timer if needed.
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_timer_del(qb_loop_t *l, qb_loop_timer_handle th);
+
+/**
+ * Check to see if a timer that is still outstanding.
+ *
+ * @param l pointer to the loop instance
+ * @param th handle to delete the timer if needed.
+ * @retval QB_TRUE yes this timer is outstanding
+ * @retval QB_FALSE this timer does not exist or has expired
+ */
+int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th);
+
+/**
+ * Get the time remaining before it expires.
+ *
+ * @note if the timer has already expired it will return 0
+ *
+ * @param l pointer to the loop instance
+ * @param th timer handle.
+ * @return nano seconds left
+ */
+uint64_t qb_loop_timer_expire_time_get(struct qb_loop *l, qb_loop_timer_handle th);
+
+/**
+ * Set a callback to receive events on file descriptors
+ * getting low.
+ * @param l pointer to the loop instance
+ * @param fn callback function.
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_poll_low_fds_event_set(qb_loop_t *l,
+				       qb_loop_poll_low_fds_event_fn fn);
+
+/**
+ * Add a poll job to the mainloop.
+ * @note it is a re-occuring job.
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param fd file descriptor.
+ * @param events (POLLIN|POLLOUT) etc ....
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_poll_add(qb_loop_t *l,
+			     enum qb_loop_priority p,
+			     int32_t fd,
+			     int32_t events,
+			     void *data,
+			     qb_loop_poll_dispatch_fn dispatch_fn);
+
+/**
+ * Modify a poll job.
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param fd file descriptor.
+ * @param events (POLLIN|POLLOUT) etc ....
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_poll_mod(qb_loop_t *l,
+			 enum qb_loop_priority p,
+			 int32_t fd,
+			 int32_t events,
+			 void *data,
+			 qb_loop_poll_dispatch_fn dispatch_fn);
+
+/**
+ * Delete a poll job.
+ *
+ * @param l pointer to the loop instance
+ * @param fd file descriptor.
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_poll_del(qb_loop_t *l, int32_t fd);
+
+/**
+ * Add a signal job.
+ *
+ * Get a callback on this signal (not in the context of the signal).
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param sig (SIGHUP or SIGINT) etc ....
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @param handle (out) a reference to the signal job
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_signal_add(qb_loop_t *l,
+			   enum qb_loop_priority p,
+			   int32_t sig,
+			   void *data,
+			   qb_loop_signal_dispatch_fn dispatch_fn,
+			   qb_loop_signal_handle *handle);
+
+/**
+ * Modify the signal job
+ *
+ * @param l pointer to the loop instance
+ * @param p the priority
+ * @param sig (SIGHUP or SIGINT) etc ....
+ * @param data user data passed into the dispatch function
+ * @param dispatch_fn callback function
+ * @param handle (in) a reference to the signal job
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_signal_mod(qb_loop_t *l,
+			   enum qb_loop_priority p,
+			   int32_t sig,
+			   void *data,
+			   qb_loop_signal_dispatch_fn dispatch_fn,
+			   qb_loop_signal_handle handle);
+
+/**
+ * Delete the signal job.
+ *
+ * @param l pointer to the loop instance
+ * @param handle (in) a reference to the signal job
+ * @return status (0 == ok, -errno == failure)
+ */
+int32_t qb_loop_signal_del(qb_loop_t *l,
+			   qb_loop_signal_handle handle);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+#endif /* QB_LOOP_H_DEFINED */
+
diff --git a/include/qb/qbmap.h b/include/qb/qbmap.h
new file mode 100644
index 0000000..172c37d
--- /dev/null
+++ b/include/qb/qbmap.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_MAP_H_DEFINED
+#define QB_MAP_H_DEFINED
+
+#include <stdint.h>
+#ifndef S_SPLINT_S
+#include <unistd.h>
+#endif /* S_SPLINT_S */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @file qbmap.h
+ * This provides a map interface to a Patricia trie, hashtable or skiplist.
+ *
+ * @par Ordering
+ * The hashtable is NOT ordered, but ptrie and skiplist are.
+ *
+ * @par Iterating
+ * Below is a simple example of how to iterate over a map.
+ * @code
+ * const char *p;
+ * void *data;
+ * qb_map_iter_t *it = qb_map_iter_create(m);
+ * for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) {
+ *     printf("%s > %s\n", p, (char*) data);
+ * }
+ * qb_map_iter_free(it);
+ * @endcode
+ *
+ * Deletion of items within the iterator is supported. But note do not
+ * free the item memory in the iterator. If you need to free the data
+ * items then register for a notifier and free the memory there. This
+ * is required as the items are reference counted.
+ * @code
+ * qb_map_notify_add(m, NULL, my_map_free_handler,
+ *		     QB_MAP_NOTIFY_FREE, NULL);
+ * @endcode
+ *
+ * @par Notifications
+ * These allow you to get callbacks when values are inserted/removed or
+ * replaced.
+ * @note hashtable only supports deletion and replacement notificatins.
+ * There is also a special global callback for freeing deleted and replaced
+ * values (QB_MAP_NOTIFY_FREE).
+ * @see qb_map_notify_add() qb_map_notify_del_2()
+ *
+ * @par Prefix matching
+ * The ptrie supports prefixes in the iterator:
+ *
+ * @code
+ * it = qb_map_pref_iter_create(m, "aa");
+ * while ((p = qb_map_iter_next(it, &data)) != NULL) {
+ *     printf("%s > %s\n", p, (char*)data);
+ * }
+ * qb_map_iter_free(it);
+ * @endcode
+ *
+ * The ptrie also supports prefixes in notifications:
+ * (remember to pass QB_MAP_NOTIFY_RECURSIVE into the notify_add.
+ * @code
+ * qb_map_notify_add(m, "root", my_map_notification,
+ *		    (QB_MAP_NOTIFY_INSERTED|
+ *		     QB_MAP_NOTIFY_DELETED|
+ *		     QB_MAP_NOTIFY_REPLACED|
+ *		     QB_MAP_NOTIFY_RECURSIVE),
+ *		    NULL);
+ *
+ * @endcode
+ */
+
+/**
+ * This is an opaque data type representing an instance of a map.
+ */
+typedef struct qb_map qb_map_t;
+
+/**
+ * This is an opaque data type representing an iterator instance.
+ */
+typedef struct qb_map_iter qb_map_iter_t;
+
+#define	QB_MAP_NOTIFY_DELETED   1
+#define	QB_MAP_NOTIFY_REPLACED  2
+#define QB_MAP_NOTIFY_INSERTED  4
+#define	QB_MAP_NOTIFY_RECURSIVE 8
+#define	QB_MAP_NOTIFY_FREE      16
+
+typedef void (*qb_map_notify_fn)(uint32_t event,
+				 char* key,
+				 void* old_value,
+				 void* value,
+				 void* user_data);
+
+typedef int32_t (*qb_map_transverse_fn)(const char* key,
+					void* value,
+					void* user_data);
+
+/**
+ * Create an unsorted map based on a hashtable.
+ *
+ * @param max_size maximum size of the hashtable
+ *
+ * @return the map instance
+ */
+qb_map_t* qb_hashtable_create(size_t max_size);
+
+/**
+ * Create a sorted map using a skiplist.
+ *
+ * @return the map instance
+ */
+qb_map_t* qb_skiplist_create(void);
+
+/**
+ * Create a sorted map using a Patricia trie or "Radix tree".
+ *
+ * @htmlonly
+ * See the wikipedia <a href="http://en.wikipedia.org/wiki/Radix_Tree">Radix_tree</a>
+ * and <a href="http://en.wikipedia.org/wiki/Trie">Trie</a> pages.
+ * @endhtmlonly
+ */
+qb_map_t* qb_trie_create(void);
+
+/**
+ * print out the nodes in the trie
+ *
+ * (for debug purposes)
+ */
+void
+qb_trie_dump(qb_map_t* m);
+
+/**
+ * Add a notifier to the map.
+ *
+ * @param m the map instance
+ * @param key the key (or prefix) to attach the notification to.
+ * @param fn the callback
+ * @param events the type of events to register for.
+ * @param user_data a pointer to be passed into the callback
+ *
+ * @note QB_MAP_NOTIFY_INSERTED is only valid on tries.
+ * @note you can use key prefixes with trie maps.
+ *
+ * @retval 0 success
+ * @retval -errno failure
+ */
+int32_t qb_map_notify_add(qb_map_t* m, const char* key,
+			  qb_map_notify_fn fn, int32_t events,
+			  void *user_data);
+
+/**
+ * Delete a notifier from the map.
+ *
+ * @note the key,fn and events must match those you added.
+ *
+ * @param m the map instance
+ * @param key the key (or prefix) to attach the notification to.
+ * @param fn the callback
+ * @param events the type of events to register for.
+ *
+ * @retval 0 success
+ * @retval -errno failure
+ */
+int32_t qb_map_notify_del(qb_map_t* m, const char* key,
+			  qb_map_notify_fn fn, int32_t events);
+
+/**
+ * Delete a notifier from the map (including the userdata).
+ *
+ * @note the key, fn, events and userdata must match those you added.
+ *
+ * @param m the map instance
+ * @param key the key (or prefix) to attach the notification to.
+ * @param fn the callback
+ * @param events the type of events to register for.
+ * @param user_data a pointer to be passed into the callback
+ *
+ * @retval 0 success
+ * @retval -errno failure
+ */
+int32_t qb_map_notify_del_2(qb_map_t* m, const char* key,
+			    qb_map_notify_fn fn, int32_t events,
+			    void *user_data);
+
+/**
+ * Inserts a new key and value into a qb_map_t.
+ *
+ * If the key already exists in the qb_map_t, it gets replaced by the new key.
+ */
+void qb_map_put(qb_map_t *map, const char* key, const void* value);
+
+/**
+ * Gets the value corresponding to the given key.
+ *
+ * @retval NULL (if the key does not exist)
+ * @retval a pointer to the value
+ */
+void* qb_map_get(qb_map_t *map, const char* key);
+
+/**
+ * Removes a key/value pair from a map.
+ */
+int32_t qb_map_rm(qb_map_t *map, const char* key);
+
+/**
+ * Get the number of items in the map.
+ */
+size_t qb_map_count_get(qb_map_t *map);
+
+/**
+ * Calls the given function for each of the key/value pairs in the map.
+ *
+ * The function is passed the key and value of each pair, and the given data
+ * parameter. The map is traversed in sorted order.
+ */
+void qb_map_foreach(qb_map_t *map, qb_map_transverse_fn func, void* user_data);
+
+/**
+ * Create an iterator
+ */
+qb_map_iter_t* qb_map_iter_create(qb_map_t *map);
+
+/**
+ * Create a prefix iterator.
+ *
+ * This will iterate over all items with the given
+ * prefix.
+ * @note this is only supported by the trie.
+ */
+qb_map_iter_t* qb_map_pref_iter_create(qb_map_t *map, const char* prefix);
+
+/**
+ * Get the next item
+ *
+ * @param i the iterator
+ * @param value (out) the next item's value
+ *
+ * @retval the next key
+ * @retval NULL - the end of the iteration
+ */
+const char* qb_map_iter_next(qb_map_iter_t* i, void** value);
+
+/**
+ * free the iterator
+ *
+ * @param i the iterator
+ */
+void qb_map_iter_free(qb_map_iter_t* i);
+
+/**
+ * Destroy the map, removes all the items from the map.
+ */
+void qb_map_destroy(qb_map_t *map);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif
+/* *INDENT-ON* */
+
+#endif /* QB_MAP_H_DEFINED */
diff --git a/include/qb/qbrb.h b/include/qb/qbrb.h
new file mode 100644
index 0000000..972d660
--- /dev/null
+++ b/include/qb/qbrb.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_RB_H_DEFINED
+#define QB_RB_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <sys/types.h>
+#include <stdint.h>
+
+/**
+ * @file qbrb.h
+ * This implements a ring buffer that works in "chunks" not bytes.
+ * So you write/read a complete chunk or not at all.
+ * There are two types of ring buffer normal and overwrite.
+ * Overwrite will reclaim the oldest chunks inorder to make way for new ones,
+ * the normal version will refuse to write a new chunk if the ring buffer
+ * is full.
+ *
+ * This implementation is capable of working across processes, but one process
+ * must only write and the other prrocess read.
+ *
+ * The read process will do the following:
+ * @code
+ *	rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS|QB_RB_FLAG_CREATE);
+ *	for (i = 0; i < 200; i++) {
+ *	try_read_again:
+ *		l = qb_rb_chunk_read(rb, (void *)out, 32, 1000);
+ *		if (l < 0) {
+ *			goto try_read_again;
+ *		}
+ *	}
+ *	...
+ *	qb_rb_close(rb);
+ * 
+ * @endcode
+ *
+ * The write process will do the following:
+ * @code
+ *	rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS);
+ *	for (i = 0; i < 200; i++) {
+ * try_write_again:
+ *		l = qb_rb_chunk_write(rb, &v, sizeof(v));
+ *		if (l < sizeof(v)) {
+ *			goto try_write_again;
+ *		}
+ *	}
+ *	...
+ *	qb_rb_close(rb);
+ * @endcode
+ *
+ * @author Angus Salkeld <asalkeld at redhat.com>
+ */
+
+/**
+ * create a ring buffer (rather than open and existing one)
+ * @see qb_rb_open()
+ */
+#define QB_RB_FLAG_CREATE		0x01
+/**
+ * New calls to qb_rb_chunk_write() will call qb_rb_chunk_reclaim()
+ * if there is not enough space.
+ * If this is not set then new writes will be refused.
+ * @see qb_rb_open()
+ */
+#define QB_RB_FLAG_OVERWRITE		0x02
+/**
+ * The ringbuffer will be shared between pthreads not processes.
+ * This effects the type of locks/semaphores that are used.
+ * @see qb_rb_open()
+ */
+#define QB_RB_FLAG_SHARED_THREAD	0x04
+/**
+ * The ringbuffer will be shared between processes.
+ * This effects the type of locks/semaphores that are used.
+ * @see qb_rb_open()
+ */
+#define QB_RB_FLAG_SHARED_PROCESS	0x08
+
+/**
+ * Don't use semaphores, only atomic ops.
+ * This mean that the timeout passed into qb_rb_chunk_read()
+ * will be ignored.
+ */
+#define QB_RB_FLAG_NO_SEMAPHORE		0x10
+
+struct qb_ringbuffer_s;
+typedef struct qb_ringbuffer_s qb_ringbuffer_t;
+
+/**
+ * Create the ring buffer with the given type.
+ *
+ * This creates allocates a ring buffer in shared memory.
+ *
+ * @param name the unique name of this ringbuffer.
+ * @param size the requested size.
+ * @param flags or'ed flags
+ * @param shared_user_data_size size for a shared data area.
+ * @note the actual size will be rounded up to the next page size.
+ * @return a new ring buffer or NULL if there was a problem.
+ * @see QB_RB_FLAG_CREATE, QB_RB_FLAG_OVERWRITE, QB_RB_FLAG_SHARED_THREAD, QB_RB_FLAG_SHARED_PROCESS
+ */
+qb_ringbuffer_t *qb_rb_open(const char *name, size_t size, uint32_t flags,
+			    size_t shared_user_data_size);
+
+/**
+ * Dereference the ringbuffer and if we are the last user destroy it.
+ *
+ * All files, mmaped memory, semaphores and locks will be destroyed.
+ *
+ * @param rb ringbuffer instance
+ */
+void qb_rb_close(qb_ringbuffer_t * rb);
+
+/**
+ * Get the name of the ringbuffer.
+ * @param rb ringbuffer instance
+ * @return name.
+ */
+char *qb_rb_name_get(qb_ringbuffer_t * rb);
+
+/**
+ * Get a point to user shared data area.
+ *
+ * @note this is of size "shared_user_data_size" passed into qb_rb_open()
+ *
+ * @param rb ringbuffer instance
+ * @return pointer to shared data.
+ */
+void *qb_rb_shared_user_data_get(qb_ringbuffer_t * rb);
+
+/**
+ * Write a chunk to the ring buffer.
+ *
+ * This simply calls qb_rb_chunk_alloc() and then
+ * qb_rb_chunk_commit().
+ *
+ * @param rb ringbuffer instance
+ * @param data (in) the data to write
+ * @param len (in) the size of the chunk.
+ * @return the amount of bytes actually buffered (either len or -1).
+ *
+ * @see qb_rb_chunk_alloc()
+ * @see qb_rb_chunk_commit()
+ */
+ssize_t qb_rb_chunk_write(qb_ringbuffer_t * rb, const void *data, size_t len);
+
+/**
+ * Allocate space for a chunk of the given size.
+ *
+ * If type == QB_RB_FLAG_OVERWRITE and NULL is returned, memory corruption of
+ * the memory file has occured. The ringbuffer should be destroyed.
+ * If type == QB_RB_NORMAL then when there is not enough space it will
+ * return NULL.
+ *
+ * @param rb ringbuffer instance
+ * @param len (in) the size to allocate.
+ * @return pointer to chunk to write to, or NULL (if no space).
+ *
+ * @see qb_rb_chunk_alloc()
+ */
+void *qb_rb_chunk_alloc(qb_ringbuffer_t * rb, size_t len);
+
+/**
+ * finalize the chunk.
+ * @param rb ringbuffer instance
+ * @param len (in) the size of the chunk.
+ */
+int32_t qb_rb_chunk_commit(qb_ringbuffer_t * rb, size_t len);
+
+/**
+ * Read (without reclaiming) the last chunk.
+ *
+ * This function is a way of accessing the next chunk without a memcpy().
+ * You can read the chunk data in place.
+ *
+ * @note This function will not "pop" the chunk, you will need to call
+ * qb_rb_chunk_reclaim().
+ * @param rb ringbuffer instance
+ * @param data_out (out) a pointer to the next chunk to read (not copied).
+ * @param ms_timeout (in) time to wait for new data.
+ *
+ * @return the size of the chunk (0 if buffer empty).
+ */
+ssize_t qb_rb_chunk_peek(qb_ringbuffer_t * rb, void **data_out,
+			 int32_t ms_timeout);
+
+/**
+ * Reclaim the oldest chunk.
+ * You will need to call this if using qb_rb_chunk_peek(). 
+ * @param rb ringbuffer instance
+ */
+void qb_rb_chunk_reclaim(qb_ringbuffer_t * rb);
+
+/**
+ * Read the oldest chunk into data_out.
+ *
+ * This is the same as qb_rb_chunk_peek() memcpy() and qb_rb_chunk_reclaim().
+ *
+ * @param rb ringbuffer instance
+ * @param data_out (in/out) the chunk will be memcpy'ed into this.
+ * @param len (in) the size of data_out.
+ * @param ms_timeout the amount od time to wait for new data.
+ * @return the size of the chunk, or error.
+ */
+ssize_t qb_rb_chunk_read(qb_ringbuffer_t * rb, void *data_out, size_t len,
+			 int32_t ms_timeout);
+
+/**
+ * Get the reference count.
+ *
+ * @param rb ringbuffer instance
+ * @return the number of references
+ */
+int32_t qb_rb_refcount_get(qb_ringbuffer_t * rb);
+
+/**
+ * The amount of free space in the ring buffer.
+ *
+ * @note Some of this space will be consumed by the chunk headers.
+ * @param rb ringbuffer instance
+ */
+ssize_t qb_rb_space_free(qb_ringbuffer_t * rb);
+
+/**
+ * The total amount of data in the buffer.
+ *
+ * @note This includes the chunk headers (8 bytes per chunk).
+ * @param rb ringbuffer instance
+ */
+ssize_t qb_rb_space_used(qb_ringbuffer_t * rb);
+
+/**
+ * The total number of chunks in the buffer.
+ *
+ * @param rb ringbuffer instance
+ */
+ssize_t qb_rb_chunks_used(qb_ringbuffer_t * rb);
+
+/**
+ * Write the contents of the Ring Buffer to file.
+ * @param fd open file to write the ringbuffer data to.
+ * @param rb ringbuffer instance
+ * @see qb_rb_create_from_file()
+ */
+ssize_t qb_rb_write_to_file(qb_ringbuffer_t * rb, int32_t fd);
+
+/**
+ * Load the saved ring buffer from file into tempory memory.
+ * @param fd file with saved ringbuffer data.
+ * @param flags same flags as passed into qb_rb_open()
+ * @return new ringbuffer instance
+ * @see qb_rb_write_to_file()
+ */
+qb_ringbuffer_t *qb_rb_create_from_file(int32_t fd, uint32_t flags);
+
+/**
+ * Like 'chown' it changes the owner and group of the ringbuffers
+ * resources.
+ * @param owner uid of the owner to change to
+ * @param group gid of the group to change to
+ * @param rb ringbuffer instance
+ * @return status (0 = ok, -errno for error)
+ */
+int32_t qb_rb_chown(qb_ringbuffer_t * rb, uid_t owner, gid_t group);
+
+/**
+ * Like 'chmod' it changes the mode of the ringbuffers resources.
+ * @param mode mode to change to
+ * @param rb ringbuffer instance
+ * @retval 0 == ok
+ * @retval -errno for error
+ */
+int32_t qb_rb_chmod(qb_ringbuffer_t * rb, mode_t mode);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+
+#endif /* QB_RB_H_DEFINED */
diff --git a/include/qb/qbutil.h b/include/qb/qbutil.h
new file mode 100644
index 0000000..87102e8
--- /dev/null
+++ b/include/qb/qbutil.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_UTIL_H_DEFINED
+#define QB_UTIL_H_DEFINED
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include <pthread.h>
+
+#include <stdint.h>
+#ifndef S_SPLINT_S
+#include <unistd.h>
+#endif /* S_SPLINT_S */
+#include <qb/qbdefs.h>
+
+/**
+ * @file qbutil.h
+ * @author Angus Salkeld <asalkeld at redhat.com>
+ *
+ * These are some convience functions used throughout libqb.
+ *
+ * @par Locking
+ * - qb_thread_lock_create()
+ * - qb_thread_lock()
+ * - qb_thread_trylock()
+ * - qb_thread_unlock()
+ * - qb_thread_lock_destroy()
+ *
+ * @par Time functions
+ * - qb_timespec_add_ms()
+ * - qb_util_nano_current_get()
+ * - qb_util_nano_monotonic_hz()
+ * - qb_util_nano_from_epoch_get()
+ * - qb_util_timespec_from_epoch_get()
+ *
+ * @par Basic Stopwatch
+ * @code
+ * uint64_t elapsed1;
+ * uint64_t elapsed2;
+ * qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+ *
+ * qb_util_stopwatch_start(sw);
+ *
+ * usleep(sometime);
+ * qb_util_stopwatch_stop(sw);
+ * elapsed1 = qb_util_stopwatch_us_elapsed_get(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_stop(sw);
+ * elapsed2 = qb_util_stopwatch_us_elapsed_get(sw);
+ *
+ * qb_util_stopwatch_free(sw);
+ * @endcode
+ *
+ * @par Stopwatch with splits
+ * Setup a stopwatch with space for 3 splits.
+ *
+ * @code
+ * uint64_t split;
+ * qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+ *
+ * qb_util_stopwatch_split_ctl(sw, 3, 0);
+ * qb_util_stopwatch_start(sw);
+ *
+ * usleep(sometime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * usleep(somemoretime);
+ * qb_util_stopwatch_split(sw);
+ *
+ * idx = qb_util_stopwatch_split_last(sw);
+ * do {
+ *      split = qb_util_stopwatch_time_split_get(sw, idx, idx);
+ *      qb_log(LOG_INFO, "split %d is %"PRIu64"", last, split);
+ *      idx--;
+ * } while (split > 0);
+ *
+ * split = qb_util_stopwatch_time_split_get(sw, 2, 1);
+ * qb_log(LOG_INFO, "time between second and third split is %"PRIu64"", split);
+ *
+ * qb_util_stopwatch_free(sw);
+ * @endcode
+ *
+ */
+
+/**
+ * @typedef qb_thread_lock_type_t
+ * QB_THREAD_LOCK_SHORT is a short term lock (spinlock if available on your system)
+ * QB_THREAD_LOCK_LONG is a mutex
+ */
+typedef enum {
+	QB_THREAD_LOCK_SHORT,
+	QB_THREAD_LOCK_LONG,
+} qb_thread_lock_type_t;
+
+struct qb_thread_lock_s;
+typedef struct qb_thread_lock_s qb_thread_lock_t;
+
+/**
+ * Create a new lock of the given type.
+ * @param type QB_THREAD_LOCK_SHORT == spinlock (where available, else mutex)
+ *        QB_THREAD_LOCK_LONG == mutex 
+ * @return pointer to qb_thread_lock_type_t or NULL on error.
+ */
+qb_thread_lock_t *qb_thread_lock_create(qb_thread_lock_type_t type);
+
+/**
+ * Calls either pthread_mutex_lock() or pthread_spin_lock().
+ */
+int32_t qb_thread_lock(qb_thread_lock_t * tl);
+
+/**
+ * Calls either pthread_mutex_trylock() or pthread_spin_trylock().
+ */
+int32_t qb_thread_trylock(qb_thread_lock_t * tl);
+
+/**
+ * Calls either pthread_mutex_unlock() or pthread_spin_unlock.
+ */
+int32_t qb_thread_unlock(qb_thread_lock_t * tl);
+
+/**
+ * Calls either pthread_mutex_destro() or pthread_spin_destroy().
+ */
+int32_t qb_thread_lock_destroy(qb_thread_lock_t * tl);
+
+typedef void (*qb_util_log_fn_t) (const char *file_name,
+				  int32_t file_line,
+				  int32_t severity, const char *msg);
+
+/**
+ * Use this function to output libqb internal log message as you wish.
+ */
+void qb_util_set_log_function(qb_util_log_fn_t fn) QB_GNUC_DEPRECATED;
+
+/**
+ * Add milliseconds onto the timespec.
+ * @param ts the ts to add to
+ * @param ms the amount of milliseconds to increment ts
+ */
+void qb_timespec_add_ms(struct timespec *ts, int32_t ms);
+
+/**
+ * Get the current number of nano secounds produced
+ * by the systems incrementing clock (CLOCK_MONOTOMIC
+ * if available).
+ */
+uint64_t qb_util_nano_current_get(void);
+
+/**
+ * Get the frequence of the clock used in
+ * qb_util_nano_current_get().
+ */
+uint64_t qb_util_nano_monotonic_hz(void);
+
+/**
+ * Get the time in nano seconds since epoch.
+ */
+uint64_t qb_util_nano_from_epoch_get(void);
+
+/**
+ * Get the time in timespec since epoch.
+ * @param ts (out) the timespec
+ * @return status (0 == ok, -errno on error)
+ */
+void qb_util_timespec_from_epoch_get(struct timespec *ts);
+
+/**
+ * strerror_r replacement.
+ */
+char *qb_strerror_r(int errnum, char *buf, size_t buflen);
+
+
+typedef struct qb_util_stopwatch qb_util_stopwatch_t;
+
+#define QB_UTIL_SW_OVERWRITE 0x01
+
+
+/**
+ * Create a Stopwatch (to time operations)
+ */
+qb_util_stopwatch_t * qb_util_stopwatch_create(void);
+
+/**
+ * Free the stopwatch
+ */
+void qb_util_stopwatch_free(qb_util_stopwatch_t *sw);
+
+/**
+ * Start the stopwatch
+ *
+ * This also acts as a reset. Essentially it sets the
+ * starting time and clears the splits.
+ */
+void qb_util_stopwatch_start(qb_util_stopwatch_t *sw);
+
+/**
+ * Stop the stopwatch
+ *
+ * This just allows you to get the elapsed time. So
+ * you can call this multiple times. Do not call qb_util_stopwatch_start()
+ * unless you want to reset the stopwatch.
+ */
+void qb_util_stopwatch_stop(qb_util_stopwatch_t *sw);
+
+/**
+ * Get the elapsed time in micro seconds.
+ *
+ * (it must have been started and stopped).
+ */
+uint64_t qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t *sw);
+
+/**
+ * Get the elapsed time in seconds.
+ *
+ * (it must have been started and stopped).
+ */
+float qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *sw);
+
+/**
+ *
+ * @param sw the stopwatch
+ * @param max_splits maximum number of time splits
+ * @param options (0 or QB_UTIL_SW_OVERWRITE )
+ * @retval 0 on success
+ * @retval -errno on failure
+ */
+int32_t qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
+        uint32_t max_splits, uint32_t options);
+
+/**
+ * Create a new time split (or lap time)
+ *
+ * @param sw the stopwatch
+ * @retval the relative split time in micro seconds
+ * @retval 0 if no more splits available
+ */
+uint64_t qb_util_stopwatch_split(qb_util_stopwatch_t *sw);
+
+/**
+ * Get the last split index to be used by
+ * qb_util_stopwatch_time_split_get()
+ *
+ * @note this is zero based
+ *
+ * @param sw the stopwatch
+ * @return the last entry index
+ */
+uint32_t
+qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw);
+
+/**
+ * Read the time split (in us) from "receint" to "older".
+ *
+ * If older == receint then the cumulated split will be
+ * returned (from the stopwatch start).
+ *
+ * @param sw the stopwatch
+ * @param receint split
+ * @param older split
+ * @retval the split time in micro seconds
+ * @retval 0 if not a valid split
+ */
+uint64_t
+qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
+				 uint32_t receint, uint32_t older);
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+/* *INDENT-ON* */
+
+#endif /* QB_UTIL_H_DEFINED */
diff --git a/include/tlist.h b/include/tlist.h
new file mode 100644
index 0000000..bcae55d
--- /dev/null
+++ b/include/tlist.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2006-2007, 2009 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_TLIST_H_DEFINED
+#define QB_TLIST_H_DEFINED
+
+#include "os_base.h"
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblist.h>
+
+#ifndef TIMER_HANDLE
+typedef void *timer_handle;
+#define TIMER_HANDLE
+#endif
+
+static int64_t timerlist_hertz;
+
+struct timerlist {
+	struct qb_list_head timer_head;
+};
+
+struct timerlist_timer {
+	struct qb_list_head list;
+	uint64_t expire_time;
+	int32_t is_absolute_timer;
+	void (*timer_fn) (void *data);
+	void *data;
+	timer_handle handle_addr;
+};
+
+static inline void timerlist_init(struct timerlist *timerlist)
+{
+	qb_list_init(&timerlist->timer_head);
+	timerlist_hertz = qb_util_nano_monotonic_hz();
+}
+
+static inline void timerlist_add(struct timerlist *timerlist,
+				 struct timerlist_timer *timer)
+{
+	struct qb_list_head *timer_list = 0;
+	struct timerlist_timer *timer_from_list;
+	int32_t found = QB_FALSE;
+
+	qb_list_for_each(timer_list, &timerlist->timer_head) {
+
+		timer_from_list = qb_list_entry(timer_list,
+						struct timerlist_timer, list);
+
+		if (timer_from_list->expire_time > timer->expire_time) {
+			qb_list_add_tail(&timer->list, timer_list);
+			found = QB_TRUE;
+			break;	/* for timer iteration */
+		}
+	}
+	if (found == QB_FALSE) {
+		qb_list_add_tail(&timer->list, &timerlist->timer_head);
+	}
+}
+
+static inline int32_t timerlist_add_duration(struct timerlist *timerlist,
+					 void (*timer_fn) (void *data),
+					 void *data,
+					 uint64_t nano_duration,
+					 timer_handle * handle)
+{
+	struct timerlist_timer *timer;
+
+	timer =
+	    (struct timerlist_timer *)malloc(sizeof(struct timerlist_timer));
+	if (timer == 0) {
+		return -ENOMEM;
+	}
+
+	timer->expire_time = qb_util_nano_current_get() + nano_duration;
+	timer->is_absolute_timer = QB_FALSE;
+	timer->data = data;
+	timer->timer_fn = timer_fn;
+	timer->handle_addr = handle;
+	timerlist_add(timerlist, timer);
+
+	*handle = timer;
+	return (0);
+}
+
+static inline void timerlist_del(struct timerlist *timerlist,
+				 timer_handle _timer_handle)
+{
+	struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle;
+
+	memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *));
+	qb_list_del(&timer->list);
+	qb_list_init(&timer->list);
+	free(timer);
+}
+
+static inline uint64_t timerlist_expire_time(struct timerlist
+						       *timerlist,
+						       timer_handle
+						       _timer_handle)
+{
+	struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle;
+
+	return (timer->expire_time);
+}
+
+static inline void timerlist_pre_dispatch(struct timerlist *timerlist,
+					  timer_handle _timer_handle)
+{
+	struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle;
+
+	memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *));
+	qb_list_del(&timer->list);
+	qb_list_init(&timer->list);
+}
+
+static inline void timerlist_post_dispatch(struct timerlist *timerlist,
+					   timer_handle _timer_handle)
+{
+	struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle;
+
+	free(timer);
+}
+
+/*
+ * returns the number of msec until the next timer will expire for use with poll
+ */
+static inline uint64_t timerlist_msec_duration_to_expire(struct timerlist *timerlist)
+{
+	struct timerlist_timer *timer_from_list;
+	volatile uint64_t current_time;
+	volatile uint64_t msec_duration_to_expire;
+
+	/*
+	 * empty list, no expire
+	 */
+	if (qb_list_empty(&timerlist->timer_head)) {
+		return (-1);
+	}
+
+	timer_from_list = qb_list_first_entry(&timerlist->timer_head,
+					struct timerlist_timer, list);
+
+	if (timer_from_list->is_absolute_timer) {
+		current_time = qb_util_nano_from_epoch_get();
+	} else {
+		current_time = qb_util_nano_current_get();
+	}
+
+	/*
+	 * timer at head of list is expired, zero msecs required
+	 */
+	if (timer_from_list->expire_time < current_time) {
+		return (0);
+	}
+
+	msec_duration_to_expire =
+	    ((timer_from_list->expire_time -
+	      current_time) / QB_TIME_NS_IN_MSEC) + (1000 / timerlist_hertz);
+	return (msec_duration_to_expire);
+}
+
+/*
+ * Expires any timers that should be expired
+ */
+static inline void timerlist_expire(struct timerlist *timerlist)
+{
+	struct timerlist_timer *timer_from_list;
+	struct qb_list_head *pos;
+	struct qb_list_head *next;
+	uint64_t current_time_from_epoch;
+	uint64_t current_monotonic_time;
+	uint64_t current_time;
+
+	current_monotonic_time = qb_util_nano_current_get();
+	current_time_from_epoch = current_time = qb_util_nano_from_epoch_get();
+
+	qb_list_for_each_safe(pos, next, &timerlist->timer_head) {
+
+		timer_from_list = qb_list_entry(pos,
+						struct timerlist_timer, list);
+
+		current_time =
+		    (timer_from_list->
+		     is_absolute_timer ? current_time_from_epoch :
+		     current_monotonic_time);
+
+		if (timer_from_list->expire_time < current_time) {
+
+			timerlist_pre_dispatch(timerlist, timer_from_list);
+
+			timer_from_list->timer_fn(timer_from_list->data);
+
+			timerlist_post_dispatch(timerlist, timer_from_list);
+		} else {
+			break;	/* for timer iteration */
+		}
+	}
+}
+#endif /* QB_TLIST_H_DEFINED */
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..bf12fd7
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1 @@
+run_splint.sh
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..37b9c23
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,86 @@
+#
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# Author: Angus Salkeld <asaslkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+
+
+MAINTAINERCLEANFILES	= Makefile.in
+
+noinst_HEADERS          = ipc_int.h util_int.h ringbuffer_int.h loop_int.h \
+			  log_int.h map_int.h rpl_sem.h loop_poll_int.h \
+			  atomic_int.h
+
+AM_CPPFLAGS             = -I$(top_builddir)/include -I$(top_srcdir)/include
+
+lib_LTLIBRARIES		= libqb.la
+
+libqb_la_LDFLAGS	= -version-number 0:17:1
+
+source_to_lint		= util.c hdb.c ringbuffer.c ringbuffer_helper.c \
+			  array.c loop.c loop_poll.c loop_job.c \
+			  loop_timerlist.c ipcc.c ipcs.c ipc_shm.c \
+			  ipc_setup.c ipc_socket.c \
+			  log.c log_thread.c log_blackbox.c log_file.c \
+			  log_syslog.c log_dcs.c log_format.c \
+			  map.c skiplist.c hashtable.c trie.c
+
+libqb_la_SOURCES	= $(source_to_lint) unix.c
+libqb_la_LIBADD	        = @LTLIBOBJS@
+
+AM_LDFLAGS 	= $(LDFLAGS_COPY:-Bsymbolic-functions=)
+
+if HAVE_SEM_TIMEDWAIT
+else
+  libqb_la_SOURCES+=rpl_sem.c
+endif
+
+if HAVE_EPOLL
+  libqb_la_SOURCES+=loop_poll_epoll.c
+else
+if HAVE_KQUEUE
+  libqb_la_SOURCES+=loop_poll_kqueue.c
+else
+  libqb_la_SOURCES+=loop_poll_poll.c
+endif
+endif
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libqb.pc
+
+if HAVE_SPLINT
+check_SCRIPTS = run_splint.sh
+TESTS = $(check_SCRIPTS)
+# this is a hack because debian/ubuntu don't set the arch path
+# in splint.
+DEB_INCLUDES = -I/usr/include/x86_64-linux-gnu -I/usr/include/i386-linux-gnu
+
+ALL_LINT_FLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(DEB_INCLUDES) \
+		 $(libqb_la_CPPFLAGS) $(CPPFLAGS) $(AM_CPPFLAGS) \
+		 $(LINT_FLAGS)
+
+run_splint.sh: $(top_srcdir)/configure.ac
+	echo "$(SPLINT) $(ALL_LINT_FLAGS) $(addprefix $(top_srcdir)/lib/, $(source_to_lint))" > $@
+	$(AM_V_GEN)chmod +x $@
+
+dist-clean-local:
+	$(AM_V_GEN)rm -f run_splint.sh
+
+clean-generic:
+	$(AM_V_GEN)rm -f run_splint.sh
+endif
diff --git a/lib/array.c b/lib/array.c
new file mode 100644
index 0000000..568990a
--- /dev/null
+++ b/lib/array.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbarray.h>
+#include <qb/qbutil.h>
+
+#define MAX_ELEMENTS_PER_BIN 16
+#define MAX_BINS 4096
+
+#define BIN_NUM_GET(_idx_) (_idx_ >> 4)
+#define ELEM_NUM_GET(_idx_) (_idx_ & 0x0F)
+
+struct qb_array {
+	void **bin;
+	size_t max_elements;
+	size_t element_size;
+	size_t num_bins;
+	size_t autogrow_elements;
+	qb_thread_lock_t *grow_lock;
+	qb_array_new_bin_cb_fn new_bin_cb;
+};
+
+qb_array_t *
+qb_array_create(size_t max_elements, size_t element_size)
+{
+	return qb_array_create_2(max_elements, element_size, 0);
+}
+
+static int32_t
+_grow_bin_array(struct qb_array * a, int32_t new_bin_size)
+{
+	int32_t b;
+
+	a->bin = realloc(a->bin, sizeof(void*) * new_bin_size);
+	if (a->bin == NULL) {
+		return -ENOMEM;
+	}
+	for (b = a->num_bins; b < new_bin_size; b++) {
+		a->bin[b] = NULL;
+	}
+	a->num_bins = new_bin_size;
+
+	return 0;
+}
+
+qb_array_t *
+qb_array_create_2(size_t max_elements, size_t element_size,
+		  size_t autogrow_elements)
+{
+	struct qb_array *a = NULL;
+	int32_t b;
+
+	if (max_elements > (MAX_ELEMENTS_PER_BIN * MAX_BINS)) {
+		errno = -EINVAL;
+		return NULL;
+	}
+	if (element_size < 1) {
+		errno = -EINVAL;
+		return NULL;
+	}
+	if (autogrow_elements > MAX_ELEMENTS_PER_BIN) {
+		errno = -EINVAL;
+		return NULL;
+	}
+	a = calloc(1, sizeof(struct qb_array));
+	if (a == NULL) {
+		return NULL;
+	}
+	a->element_size = element_size;
+	a->max_elements = max_elements;
+	b = QB_MIN((max_elements / MAX_ELEMENTS_PER_BIN) + 1, MAX_BINS);
+	a->autogrow_elements = autogrow_elements;
+	a->bin = NULL;
+	if (_grow_bin_array(a, b) < 0) {
+		free(a);
+		return NULL;
+	}
+	a->grow_lock = qb_thread_lock_create(QB_THREAD_LOCK_SHORT);
+	return a;
+}
+
+int32_t
+qb_array_index(struct qb_array * a, int32_t idx, void **element_out)
+{
+	int32_t b;
+	int32_t elem;
+	char *bin;
+	int32_t rc = 0;
+
+	if (a == NULL || element_out == NULL) {
+		return -EINVAL;
+	}
+	if (idx < 0) {
+		return -ERANGE;
+	}
+	if (idx >= a->max_elements) {
+		if (a->autogrow_elements == 0) {
+			return -ERANGE;
+		} else {
+			rc = qb_array_grow(a, idx + 1);
+			if (rc != 0) {
+				return rc;
+			}
+		}
+	}
+	b = BIN_NUM_GET(idx);
+	assert(b < MAX_BINS);
+
+	if (b >= a->num_bins || a->bin[b] == NULL) {
+		int32_t bin_alloced = QB_FALSE;
+
+		(void)qb_thread_lock(a->grow_lock);
+
+		if (b >= a->num_bins) {
+			rc = _grow_bin_array(a, b + 1);
+			if (rc < 0) {
+				goto unlock_error;
+			}
+		}
+		if (a->bin[b] == NULL) {
+			a->bin[b] = calloc(MAX_ELEMENTS_PER_BIN, a->element_size);
+			if (a->bin[b] == NULL) {
+				rc = -errno;
+				goto unlock_error;
+			}
+			bin_alloced = QB_TRUE;
+		}
+
+		(void)qb_thread_unlock(a->grow_lock);
+		if (bin_alloced && a->new_bin_cb) {
+			a->new_bin_cb(a, b);
+		}
+	}
+
+	elem = ELEM_NUM_GET(idx);
+	assert(elem < MAX_ELEMENTS_PER_BIN);
+
+	bin = a->bin[b];
+	*element_out = (bin + (a->element_size * elem));
+
+	return 0;
+
+unlock_error:
+
+	(void)qb_thread_unlock(a->grow_lock);
+	return rc;
+}
+
+int32_t
+qb_array_new_bin_cb_set(struct qb_array * a, qb_array_new_bin_cb_fn fn)
+{
+	if (a == NULL) {
+		return -EINVAL;
+	}
+	a->new_bin_cb = fn;
+	return 0;
+}
+
+size_t
+qb_array_num_bins_get(struct qb_array * a)
+{
+	if (a == NULL) {
+		return -EINVAL;
+	}
+	return a->num_bins;
+}
+
+size_t
+qb_array_elems_per_bin_get(struct qb_array * a)
+{
+	if (a == NULL) {
+		return -EINVAL;
+	}
+	return MAX_ELEMENTS_PER_BIN;
+}
+
+int32_t
+qb_array_grow(struct qb_array * a, size_t max_elements)
+{
+	int32_t b;
+	int32_t rc = 0;
+
+	if (a == NULL || max_elements > (MAX_ELEMENTS_PER_BIN * MAX_BINS)) {
+		return -EINVAL;
+	}
+	if (max_elements <= a->max_elements) {
+		return 0;
+	}
+	a->max_elements = max_elements;
+	b = QB_MIN((max_elements / MAX_ELEMENTS_PER_BIN) + 1, MAX_BINS);
+	if (b > a->num_bins) {
+		(void)qb_thread_lock(a->grow_lock);
+		if (b >= a->num_bins) {
+			rc = _grow_bin_array(a, b + 1);
+		}
+		(void)qb_thread_unlock(a->grow_lock);
+	}
+	return rc;
+}
+
+void
+qb_array_free(struct qb_array *a)
+{
+	int32_t i;
+	for (i = 0; i < a->num_bins; i++) {
+		free(a->bin[i]);
+	}
+	free(a->bin);
+	(void)qb_thread_lock_destroy(a->grow_lock);
+	free(a);
+}
diff --git a/lib/atomic_int.h b/lib/atomic_int.h
new file mode 100644
index 0000000..63f49fd
--- /dev/null
+++ b/lib/atomic_int.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_ATOMIC_INT_H_DEFINED
+#define QB_ATOMIC_INT_H_DEFINED
+
+/*
+ * This adds some extra atomic functionality, building on the
+ * gcc atomic builtins.
+ */
+
+#include "os_base.h"
+#include <qb/qbdefs.h>
+#include <qb/qbatomic.h>
+
+/* This is a thin wrapper around the new gcc atomics.
+ */
+enum qb_atomic_model {
+	QB_ATOMIC_RELAXED,
+	QB_ATOMIC_CONSUME,
+	QB_ATOMIC_ACQUIRE,
+	QB_ATOMIC_RELEASE,
+	QB_ATOMIC_ACQ_REL,
+	QB_ATOMIC_SEQ_CST,
+};
+
+#ifdef HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS
+static inline int
+qb_model_map(enum qb_atomic_model model)
+{
+	switch (model) {
+	case QB_ATOMIC_ACQUIRE:
+		return __ATOMIC_ACQUIRE;
+	case QB_ATOMIC_RELEASE:
+		return __ATOMIC_RELEASE;
+	case QB_ATOMIC_RELAXED:
+		return __ATOMIC_RELAXED;
+	case QB_ATOMIC_CONSUME:
+		return __ATOMIC_CONSUME;
+	case QB_ATOMIC_ACQ_REL:
+		return __ATOMIC_ACQ_REL;
+	case QB_ATOMIC_SEQ_CST:
+	default:
+		return __ATOMIC_SEQ_CST;
+	}
+}
+#endif /* HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS */
+
+#ifdef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+
+#ifdef HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS
+#define QB_ATOMIC_MEMORY_BARRIER __sync_synchronize ()
+#else
+#ifndef HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS
+#warning you need memory barriers but do not have an implementation.
+#endif /* HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS */
+#endif /* HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS */
+
+#ifndef QB_ATOMIC_MEMORY_BARRIER
+#define QB_ATOMIC_MEMORY_BARRIER
+#endif /* QB_ATOMIC_MEMORY_BARRIER */
+
+#endif /* QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
+
+/**
+ * Reads the value of the integer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param model the memory model to use.
+ *
+ * @return the value of atomic
+ */
+static inline int32_t
+qb_atomic_int_get_ex(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+		     enum qb_atomic_model model)
+{
+#ifdef HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS
+	return __atomic_load_n(atomic, qb_model_map(model));
+#else
+	return qb_atomic_int_get(atomic);
+#endif
+}
+
+
+/**
+ * Sets the value of the integer pointed to by atomic.
+ * Also acts as a memory barrier.
+ *
+ * @param atomic a pointer to an integer
+ * @param newval the new value
+ * @param model the memory model to use.
+ */
+static inline void
+qb_atomic_int_set_ex(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+		     int32_t newval,
+		     enum qb_atomic_model model)
+{
+#ifdef HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS
+	__atomic_store_n(atomic, newval, qb_model_map(model));
+#else
+/*
+ * If the model is acquire we need the barrier afterwards,
+ * and if its cst we need it before and after.
+ * Note: qb_atomic_int_set already has a memory barrier after
+ * the set.
+ */
+	if (model != QB_ATOMIC_RELAXED && model != QB_ATOMIC_CONSUME) {
+		QB_ATOMIC_MEMORY_BARRIER;
+	}
+	qb_atomic_int_set(atomic, newval);
+#endif
+}
+
+#endif /* QB_ATOMIC_INT_H_DEFINED */
diff --git a/lib/hashtable.c b/lib/hashtable.c
new file mode 100644
index 0000000..59c4dcd
--- /dev/null
+++ b/lib/hashtable.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbmap.h>
+#include "util_int.h"
+#include "map_int.h"
+#include <qb/qblist.h>
+
+#define FNV_32_PRIME ((uint32_t)0x01000193)
+
+struct hash_node {
+	struct qb_list_head list;
+	void *value;
+	const char *key;
+	uint32_t refcount;
+	struct qb_list_head notifier_head;
+};
+
+struct hash_bucket {
+	struct qb_list_head list_head;
+};
+
+struct hash_table {
+	struct qb_map map;
+	size_t count;
+	uint32_t order;
+	uint32_t hash_buckets_len;
+	struct qb_list_head notifier_head;
+	struct hash_bucket hash_buckets[0];
+};
+
+struct hashtable_iter {
+	struct qb_map_iter i;
+	struct hash_node *node;
+	uint32_t bucket;
+};
+
+static void hashtable_notify(struct hash_table *t, struct hash_node *n,
+			     uint32_t event, const char *key,
+			     void *old_value, void *value);
+static void hashtable_node_deref_under_bucket(struct qb_map *map,
+					      int32_t hash_entry);
+static void hashtable_node_destroy(struct hash_table *t,
+				   struct hash_node *hash_node);
+
+static uint32_t
+hash_fnv(const void *value, uint32_t valuelen, uint32_t order)
+{
+	uint8_t *cd = (uint8_t *) value;
+	uint8_t *ed = (uint8_t *) value + valuelen;
+	uint32_t hash_result = 0x811c9dc5;
+	int32_t res;
+
+	while (cd < ed) {
+		hash_result ^= *cd;
+		hash_result *= FNV_32_PRIME;
+		cd++;
+	}
+	res = ((hash_result >> order) ^ hash_result) & ((1 << order) - 1);
+	return (res);
+}
+
+static uint32_t
+qb_hash_string(const void *key, uint32_t order)
+{
+	char *str = (char *)key;
+	return hash_fnv(key, strlen(str), order);
+}
+
+static struct hash_node *
+hashtable_lookup(struct hash_table *t, const char *key)
+{
+	uint32_t hash_entry;
+	struct qb_list_head *list;
+	struct hash_node *hash_node;
+
+	hash_entry = qb_hash_string(key, t->order);
+
+	qb_list_for_each(list, &t->hash_buckets[hash_entry].list_head) {
+
+		hash_node = qb_list_entry(list, struct hash_node, list);
+		if (strcmp(hash_node->key, key) == 0) {
+			return hash_node;
+		}
+	}
+
+	return NULL;
+}
+
+static void *
+hashtable_get(struct qb_map *map, const char *key)
+{
+	struct hash_table *t = (struct hash_table *)map;
+	struct hash_node *n = hashtable_lookup(t, key);
+	if (n) {
+		return n->value;
+	}
+	return NULL;
+}
+
+static void
+hashtable_node_destroy(struct hash_table *t, struct hash_node *hash_node)
+{
+	struct qb_list_head *pos;
+	struct qb_list_head *next;
+	struct qb_map_notifier *tn;
+
+	hashtable_notify(t, hash_node,
+			 QB_MAP_NOTIFY_DELETED,
+			 hash_node->key, hash_node->value, NULL);
+
+	qb_list_for_each_safe(pos, next, &hash_node->notifier_head) {
+		tn = qb_list_entry(pos, struct qb_map_notifier, list);
+		qb_list_del(&tn->list);
+		free(tn);
+	}
+
+	qb_list_del(&hash_node->list);
+	free(hash_node);
+}
+
+static void
+hashtable_node_deref(struct qb_map *map, struct hash_node *hash_node)
+{
+	struct hash_table *t = (struct hash_table *)map;
+
+	hash_node->refcount--;
+	if (hash_node->refcount > 0) {
+		return;
+	}
+	hashtable_node_destroy(t, hash_node);
+}
+
+static int32_t
+hashtable_rm_with_hash(struct qb_map *map, const char *key, uint32_t hash_entry)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	struct qb_list_head *list;
+	struct qb_list_head *next;
+	struct hash_node *hash_node;
+
+	qb_list_for_each_safe(list, next,
+	                      &hash_table->hash_buckets[hash_entry].list_head) {
+
+		hash_node = qb_list_entry(list, struct hash_node, list);
+		if (strcmp(hash_node->key, key) == 0) {
+			hashtable_node_deref(map, hash_node);
+			hash_table->count--;
+			return QB_TRUE;
+		}
+	}
+
+	return QB_FALSE;
+}
+
+static int32_t
+hashtable_rm(struct qb_map *map, const char *key)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	uint32_t hash_entry;
+
+	hash_entry = qb_hash_string(key, hash_table->order);
+	return hashtable_rm_with_hash(map, key, hash_entry);
+}
+
+static void
+hashtable_put(struct qb_map *map, const char *key, const void *value)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	uint32_t hash_entry;
+	struct hash_node *hash_node = NULL;
+	struct hash_node *node_try;
+	struct qb_list_head *list;
+
+	hash_entry = qb_hash_string(key, hash_table->order);
+
+	qb_list_for_each(list, &hash_table->hash_buckets[hash_entry].list_head) {
+
+		node_try = qb_list_entry(list, struct hash_node, list);
+		if (strcmp(node_try->key, key) == 0) {
+			hash_node = node_try;
+			break;
+		}
+	}
+
+	if (hash_node == NULL) {
+		hash_node = calloc(1, sizeof(struct hash_node));
+		if (hash_node == NULL) {
+			errno = ENOMEM;
+			return;
+		}
+
+		hash_table->count++;
+		hash_node->refcount = 1;
+		hash_node->key = key;
+		hash_node->value = (void *)value;
+		qb_list_init(&hash_node->list);
+		qb_list_add_tail(&hash_node->list,
+				 &hash_table->hash_buckets[hash_entry].
+				 list_head);
+		qb_list_init(&hash_node->notifier_head);
+
+		hashtable_notify(hash_table, hash_node,
+				 QB_MAP_NOTIFY_INSERTED,
+				 hash_node->key, NULL, hash_node->value);
+	} else {
+		char *old_k = (char *)hash_node->key;
+		char *old_v = (void *)hash_node->value;
+		hash_node->key = key;
+		hash_node->value = (void *)value;
+
+		hashtable_notify(hash_table, hash_node,
+				 QB_MAP_NOTIFY_REPLACED,
+				 old_k, old_v, hash_node->value);
+	}
+}
+
+static void
+hashtable_notify(struct hash_table *t, struct hash_node *n,
+		 uint32_t event, const char *key,
+		 void *old_value, void *value)
+{
+	struct qb_list_head *list;
+	struct qb_map_notifier *tn;
+
+	qb_list_for_each(list, &n->notifier_head) {
+		tn = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (tn->events & event) {
+			tn->callback(event, (char *)key, old_value, value,
+				     tn->user_data);
+		}
+	}
+	qb_list_for_each(list, &t->notifier_head) {
+		tn = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (tn->events & event) {
+			tn->callback(event, (char *)key, old_value, value,
+				     tn->user_data);
+		}
+		if (((event & QB_MAP_NOTIFY_DELETED) ||
+		     (event & QB_MAP_NOTIFY_REPLACED)) &&
+		    (tn->events & QB_MAP_NOTIFY_FREE)) {
+			tn->callback(QB_MAP_NOTIFY_FREE, (char *)key,
+				     old_value, value, tn->user_data);
+		}
+	}
+}
+
+static int32_t
+hashtable_notify_add(qb_map_t * m, const char *key,
+		     qb_map_notify_fn fn, int32_t events, void *user_data)
+{
+	struct hash_table *t = (struct hash_table *)m;
+	struct qb_map_notifier *f;
+	struct hash_node *n;
+	struct qb_list_head *head = NULL;
+	struct qb_list_head *list;
+	int add_to_tail = QB_FALSE;
+
+	if (key) {
+		n = hashtable_lookup(t, key);
+		if (n) {
+			head = &n->notifier_head;
+		}
+	} else {
+		head = &t->notifier_head;
+	}
+	if (head == NULL) {
+		return -ENOENT;
+	}
+	if (events & QB_MAP_NOTIFY_FREE) {
+		add_to_tail = QB_TRUE;
+	}
+
+	qb_list_for_each(list, head) {
+		f = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (events & QB_MAP_NOTIFY_FREE &&
+		    f->events == events) {
+			/* only one free notifier */
+			return -EEXIST;
+		}
+		if (f->events == events &&
+		    f->user_data == user_data &&
+		    f->callback == fn) {
+			return -EEXIST;
+		}
+	}
+
+	f = malloc(sizeof(struct qb_map_notifier));
+	if (f == NULL) {
+		return -errno;
+	}
+	f->events = events;
+	f->user_data = user_data;
+	f->callback = fn;
+	qb_list_init(&f->list);
+
+	if (add_to_tail) {
+		qb_list_add_tail(&f->list, head);
+	} else {
+		qb_list_add(&f->list, head);
+	}
+	return 0;
+}
+
+static int32_t
+hashtable_notify_del(qb_map_t * m, const char *key,
+		     qb_map_notify_fn fn, int32_t events,
+		     int32_t cmp_userdata, void *user_data)
+{
+	struct hash_table *t = (struct hash_table *)m;
+	struct qb_map_notifier *f;
+	struct hash_node *n;
+	struct qb_list_head *head = NULL;
+	struct qb_list_head *list;
+	struct qb_list_head *next;
+	int32_t found = QB_FALSE;
+
+	if (key) {
+		n = hashtable_lookup(t, key);
+		if (n) {
+			head = &n->notifier_head;
+		}
+	} else {
+		head = &t->notifier_head;
+	}
+	if (head == NULL) {
+		return -ENOENT;
+	}
+
+	qb_list_for_each_safe(list, next, head) {
+		f = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (f->events == events && f->callback == fn) {
+			if (cmp_userdata && (f->user_data == user_data)) {
+				found = QB_TRUE;
+				qb_list_del(&f->list);
+				free(f);
+			} else if (!cmp_userdata) {
+				found = QB_TRUE;
+				qb_list_del(&f->list);
+				free(f);
+			}
+		}
+	}
+	if (found) {
+		return 0;
+	} else {
+		return -ENOENT;
+	}
+}
+
+static size_t
+hashtable_count_get(struct qb_map *map)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	return hash_table->count;
+}
+
+static qb_map_iter_t *
+hashtable_iter_create(struct qb_map *map, const char *prefix)
+{
+	struct hashtable_iter *i = malloc(sizeof(struct hashtable_iter));
+	if (i == NULL) {
+		return NULL;
+	}
+	i->i.m = map;
+	i->node = NULL;
+	i->bucket = 0;
+	return (qb_map_iter_t *) i;
+}
+
+static const char *
+hashtable_iter_next(qb_map_iter_t * it, void **value)
+{
+	struct hashtable_iter *hi = (struct hashtable_iter *)it;
+	struct hash_table *hash_table = (struct hash_table *)hi->i.m;
+	struct qb_list_head *ln;
+	struct hash_node *hash_node = NULL;
+	int found = QB_FALSE;
+	int cont = QB_TRUE;
+	int b;
+
+	if (hi->node == NULL) {
+		cont = QB_FALSE;
+	}
+	for (b = hi->bucket; b < hash_table->hash_buckets_len && !found; b++) {
+		if (cont) {
+			ln = &hi->node->list;
+			cont = QB_FALSE;
+		} else {
+			ln = &hash_table->hash_buckets[b].list_head;
+		}
+		hash_node = qb_list_first_entry(ln, struct hash_node, list);
+		qb_list_for_each_entry_from(hash_node,
+		                &hash_table->hash_buckets[b].list_head, list) {
+			if (hash_node->refcount > 0) {
+				found = QB_TRUE;
+				hash_node->refcount++;
+				hi->bucket = b;
+				*value = hash_node->value;
+				break;
+			}
+		}
+	}
+
+	if (hi->node) {
+		hashtable_node_deref(hi->i.m, hi->node);
+	}
+	if (!found) {
+		return NULL;
+	}
+	hi->node = hash_node;
+	return hash_node->key;
+}
+
+static void
+hashtable_iter_free(qb_map_iter_t * i)
+{
+	free(i);
+}
+
+static void
+hashtable_destroy(struct qb_map *map)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	struct qb_list_head *pos;
+	struct qb_list_head *next;
+	struct qb_map_notifier *tn;
+	int32_t i;
+
+	for (i = 0; i < hash_table->hash_buckets_len; i++) {
+		hashtable_node_deref_under_bucket(map, i);
+	}
+
+	qb_list_for_each_safe(pos, next, &hash_table->notifier_head) {
+		tn = qb_list_entry(pos, struct qb_map_notifier, list);
+		qb_list_del(&tn->list);
+		free(tn);
+	}
+
+	free(hash_table);
+}
+
+static void
+hashtable_node_deref_under_bucket(struct qb_map *map, int32_t hash_entry)
+{
+	struct hash_table *hash_table = (struct hash_table *)map;
+	struct hash_node *hash_node;
+	struct qb_list_head *pos;
+	struct qb_list_head *next;
+
+	qb_list_for_each_safe(pos, next,
+			      &hash_table->hash_buckets[hash_entry].list_head) {
+		hash_node = qb_list_entry(pos, struct hash_node, list);
+		hashtable_node_deref(map, hash_node);
+		hash_table->count--;
+	}
+}
+
+qb_map_t *
+qb_hashtable_create(size_t max_size)
+{
+	int32_t i;
+	int32_t order;
+	int32_t n = max_size;
+	uint64_t size;
+	struct hash_table *ht;
+
+	for (i = 0; n; i++) {
+		n >>= 1;
+	}
+	order = QB_MAX(i, 3);
+
+	size = sizeof(struct hash_table) +
+	    (sizeof(struct hash_bucket) * (1 << order));
+
+	ht = calloc(1, size);
+	if (ht == NULL) {
+		return NULL;
+	}
+
+	ht->map.put = hashtable_put;
+	ht->map.get = hashtable_get;
+	ht->map.rm = hashtable_rm;
+	ht->map.count_get = hashtable_count_get;
+	ht->map.iter_create = hashtable_iter_create;
+	ht->map.iter_next = hashtable_iter_next;
+	ht->map.iter_free = hashtable_iter_free;
+	ht->map.destroy = hashtable_destroy;
+	ht->map.notify_add = hashtable_notify_add;
+	ht->map.notify_del = hashtable_notify_del;
+	ht->count = 0;
+	ht->order = order;
+	qb_list_init(&ht->notifier_head);
+
+	ht->hash_buckets_len = 1 << order;
+	for (i = 0; i < ht->hash_buckets_len; i++) {
+		qb_list_init(&ht->hash_buckets[i].list_head);
+	}
+	return (qb_map_t *) ht;
+}
diff --git a/lib/hdb.c b/lib/hdb.c
new file mode 100644
index 0000000..be227fc
--- /dev/null
+++ b/lib/hdb.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbhdb.h>
+#include <qb/qbatomic.h>
+
+enum QB_HDB_HANDLE_STATE {
+	QB_HDB_HANDLE_STATE_EMPTY,
+	QB_HDB_HANDLE_STATE_PENDINGREMOVAL,
+	QB_HDB_HANDLE_STATE_ACTIVE
+};
+
+static void
+qb_hdb_create_first_run(struct qb_hdb *hdb)
+{
+	if (hdb->first_run == QB_TRUE) {
+		hdb->first_run = QB_FALSE;
+		qb_atomic_init();
+		hdb->handles = qb_array_create(32, sizeof(struct qb_hdb_handle));
+	}
+}
+
+void
+qb_hdb_create(struct qb_hdb *hdb)
+{
+	memset(hdb, 0, sizeof(struct qb_hdb));
+	hdb->first_run = QB_TRUE;
+	qb_hdb_create_first_run(hdb);
+}
+
+void
+qb_hdb_destroy(struct qb_hdb *hdb)
+{
+	qb_array_free(hdb->handles);
+	memset(hdb, 0, sizeof(struct qb_hdb));
+}
+
+int32_t
+qb_hdb_handle_create(struct qb_hdb *hdb, int32_t instance_size,
+		     qb_handle_t * handle_id_out)
+{
+	int32_t handle;
+	int32_t res = 0;
+	uint32_t check;
+	int32_t found = QB_FALSE;
+	void *instance;
+	int32_t i;
+	struct qb_hdb_handle *entry = NULL;
+	int32_t handle_count;
+
+	qb_hdb_create_first_run(hdb);
+
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	for (handle = 0; handle < handle_count; handle++) {
+		if (qb_array_index(hdb->handles, handle, (void**)&entry) == 0 &&
+		    entry->state == QB_HDB_HANDLE_STATE_EMPTY) {
+			found = QB_TRUE;
+			qb_atomic_int_inc(&entry->ref_count);
+			break;
+		}
+	}
+
+	if (found == QB_FALSE) {
+		res = qb_array_grow(hdb->handles, handle_count + 1);
+		if (res != 0) {
+			return res;
+		}
+		res = qb_array_index(hdb->handles, handle_count,
+				     (void **)&entry);
+		if (res != 0) {
+			return res;
+		}
+		qb_atomic_int_inc((int32_t *)&hdb->handle_count);
+	}
+
+	instance = malloc(instance_size);
+	if (instance == 0) {
+		return -ENOMEM;
+	}
+
+	/*
+	 * This code makes sure the random number isn't zero
+	 * We use 0 to specify an invalid handle out of the 1^64 address space
+	 * If we get 0 200 times in a row, the RNG may be broken
+	 */
+	for (i = 0; i < 200; i++) {
+		check = random();
+
+		if (check != 0 && check != 0xffffffff) {
+			break;
+		}
+	}
+
+	memset(instance, 0, instance_size);
+
+	entry->state = QB_HDB_HANDLE_STATE_ACTIVE;
+	entry->instance = instance;
+	entry->ref_count = 1;
+	entry->check = check;
+
+	*handle_id_out = (((uint64_t) (check)) << 32) | handle;
+
+	return res;
+}
+
+int32_t
+qb_hdb_handle_get(struct qb_hdb * hdb, qb_handle_t handle_in, void **instance)
+{
+	uint32_t check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	uint32_t handle = handle_in & 0xffffffff;
+	struct qb_hdb_handle *entry;
+	int32_t handle_count;
+
+	qb_hdb_create_first_run(hdb);
+
+	*instance = NULL;
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	if (handle >= handle_count) {
+		return (-EBADF);
+	}
+
+	if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
+	    entry->state != QB_HDB_HANDLE_STATE_ACTIVE) {
+		return (-EBADF);
+	}
+
+	if (check != 0xffffffff && check != entry->check) {
+		return (-EBADF);
+	}
+	qb_atomic_int_inc(&entry->ref_count);
+
+	*instance = entry->instance;
+
+	return (0);
+}
+
+int32_t
+qb_hdb_handle_get_always(struct qb_hdb * hdb, qb_handle_t handle_in,
+			 void **instance)
+{
+	return qb_hdb_handle_get(hdb, handle_in, instance);
+}
+
+int32_t
+qb_hdb_handle_put(struct qb_hdb * hdb, qb_handle_t handle_in)
+{
+	uint32_t check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	uint32_t handle = handle_in & 0xffffffff;
+	struct qb_hdb_handle *entry;
+	int32_t handle_count;
+
+	qb_hdb_create_first_run(hdb);
+
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	if (handle >= handle_count) {
+		return (-EBADF);
+	}
+
+	if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
+	    (check != 0xffffffff && check != entry->check)) {
+		return (-EBADF);
+	}
+
+	if (qb_atomic_int_dec_and_test(&entry->ref_count)) {
+		if (hdb->destructor) {
+			hdb->destructor(entry->instance);
+		}
+		free(entry->instance);
+		memset(entry, 0, sizeof(struct qb_hdb_handle));
+	}
+	return (0);
+}
+
+int32_t
+qb_hdb_handle_destroy(struct qb_hdb * hdb, qb_handle_t handle_in)
+{
+	uint32_t check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	uint32_t handle = handle_in & 0xffffffff;
+	int32_t res;
+	struct qb_hdb_handle *entry;
+	int32_t handle_count;
+
+	qb_hdb_create_first_run(hdb);
+
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	if (handle >= handle_count) {
+		return (-EBADF);
+	}
+
+	if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
+	    (check != 0xffffffff && check != entry->check)) {
+		return (-EBADF);
+	}
+
+	entry->state = QB_HDB_HANDLE_STATE_PENDINGREMOVAL;
+	res = qb_hdb_handle_put(hdb, handle_in);
+	return (res);
+}
+
+int32_t
+qb_hdb_handle_refcount_get(struct qb_hdb * hdb, qb_handle_t handle_in)
+{
+	uint32_t check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	uint32_t handle = handle_in & 0xffffffff;
+	struct qb_hdb_handle *entry;
+	int32_t handle_count;
+	int32_t refcount = 0;
+
+	qb_hdb_create_first_run(hdb);
+
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	if (handle >= handle_count) {
+		return (-EBADF);
+	}
+
+	if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
+	    (check != 0xffffffff && check != entry->check)) {
+		return (-EBADF);
+	}
+
+	refcount = qb_atomic_int_get(&entry->ref_count);
+
+	return (refcount);
+}
+
+void
+qb_hdb_iterator_reset(struct qb_hdb *hdb)
+{
+	hdb->iterator = 0;
+}
+
+int32_t
+qb_hdb_iterator_next(struct qb_hdb *hdb, void **instance, qb_handle_t * handle)
+{
+	int32_t res = -1;
+	uint64_t checker;
+	struct qb_hdb_handle *entry;
+	int32_t handle_count;
+
+	handle_count = qb_atomic_int_get(&hdb->handle_count);
+	while (hdb->iterator < handle_count) {
+		res = qb_array_index(hdb->handles,
+				     hdb->iterator,
+				     (void **)&entry);
+		if (res != 0) {
+			break;
+		}
+		checker = (uint64_t) (entry->check);
+		*handle = (checker << 32) | hdb->iterator;
+		res = qb_hdb_handle_get(hdb, *handle, instance);
+
+		hdb->iterator += 1;
+		if (res == 0) {
+			break;
+		}
+	}
+	return (res);
+}
+
+uint32_t
+qb_hdb_base_convert(qb_handle_t handle)
+{
+	return (handle & 0xffffffff);
+}
+
+uint64_t
+qb_hdb_nocheck_convert(uint32_t handle)
+{
+	uint64_t retvalue = 0xffffffffULL << 32 | handle;
+
+	return (retvalue);
+}
diff --git a/lib/ipc_int.h b/lib/ipc_int.h
new file mode 100644
index 0000000..c22417b
--- /dev/null
+++ b/lib/ipc_int.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *         Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_IPC_INT_H_DEFINED
+#define QB_IPC_INT_H_DEFINED
+
+#include "os_base.h"
+
+#include <dirent.h>
+#include <qb/qblist.h>
+#include <qb/qbloop.h>
+#include <qb/qbipcc.h>
+#include <qb/qbipcs.h>
+#include <qb/qbipc_common.h>
+#include <qb/qbrb.h>
+
+#define QB_IPC_MAX_WAIT_MS 2000
+
+/*
+Client		Server
+SEND CONN REQ ->
+		ACCEPT & CREATE queues
+		or DENY
+	<-	SEND ACCEPT(with details)/DENY
+*/
+
+struct qb_ipc_connection_request {
+	struct qb_ipc_request_header hdr;
+	uint32_t max_msg_size;
+} __attribute__ ((aligned(8)));
+
+struct qb_ipc_event_connection_request {
+	struct qb_ipc_request_header hdr;
+	intptr_t connection;
+} __attribute__ ((aligned(8)));
+
+struct qb_ipc_connection_response {
+	struct qb_ipc_response_header hdr;
+	int32_t connection_type;
+	uint32_t max_msg_size;
+	intptr_t connection;
+	char request[PATH_MAX];
+	char response[PATH_MAX];
+	char event[PATH_MAX];
+} __attribute__ ((aligned(8)));
+
+struct qb_ipcc_connection;
+
+struct qb_ipc_one_way {
+	size_t max_msg_size;
+	enum qb_ipc_type type;
+	union {
+		struct {
+			int32_t sock;
+			char *sock_name;
+			void* shared_data;
+			char shared_file_name[NAME_MAX];
+		} us;
+		struct {
+			qb_ringbuffer_t *rb;
+		} shm;
+	} u;
+};
+
+struct qb_ipcc_funcs {
+	ssize_t (*recv)(struct qb_ipc_one_way *one_way, void *buf, size_t buf_size, int32_t timeout);
+	ssize_t (*send)(struct qb_ipc_one_way *one_way, const void *data, size_t size);
+	ssize_t (*sendv)(struct qb_ipc_one_way *one_way, const struct iovec *iov, size_t iov_len);
+	void (*disconnect)(struct qb_ipcc_connection* c);
+	int32_t (*fc_get)(struct qb_ipc_one_way *one_way);
+};
+
+struct qb_ipcc_connection {
+	char name[NAME_MAX];
+	int32_t needs_sock_for_poll;
+	struct qb_ipc_one_way setup;
+	struct qb_ipc_one_way request;
+	struct qb_ipc_one_way response;
+	struct qb_ipc_one_way event;
+	struct qb_ipcc_funcs funcs;
+	struct qb_ipc_request_header *receive_buf;
+	uint32_t fc_enable_max;
+	int32_t is_connected;
+	void * context;
+};
+
+int32_t qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,
+				   struct qb_ipc_connection_response *r);
+ssize_t qb_ipc_us_send(struct qb_ipc_one_way *one_way, const void *msg, size_t len);
+ssize_t qb_ipc_us_recv(struct qb_ipc_one_way *one_way, void *msg, size_t len, int32_t timeout);
+int32_t qb_ipc_us_ready(struct qb_ipc_one_way *ow_data, struct qb_ipc_one_way *ow_conn,
+			int32_t ms_timeout, int32_t events);
+
+void qb_ipcc_us_sock_close(int32_t sock);
+
+int32_t qb_ipcc_us_connect(struct qb_ipcc_connection *c, struct qb_ipc_connection_response * response);
+int32_t qb_ipcc_shm_connect(struct qb_ipcc_connection *c, struct qb_ipc_connection_response * response);
+
+struct qb_ipcs_service;
+struct qb_ipcs_connection;
+
+struct qb_ipcs_funcs {
+	int32_t (*connect)(struct qb_ipcs_service *s, struct qb_ipcs_connection *c,
+		struct qb_ipc_connection_response *r);
+	void (*disconnect)(struct qb_ipcs_connection *c);
+	ssize_t (*recv)(struct qb_ipc_one_way *one_way, void *buf, size_t buf_size, int32_t timeout);
+	ssize_t (*peek)(struct qb_ipc_one_way *one_way, void **data_out, int32_t timeout);
+	void (*reclaim)(struct qb_ipc_one_way *one_way);
+	ssize_t (*send)(struct qb_ipc_one_way *one_way, const void *data, size_t size);
+	ssize_t (*sendv)(struct qb_ipc_one_way *one_way, const struct iovec* iov, size_t iov_len);
+	void (*fc_set)(struct qb_ipc_one_way *one_way, int32_t fc_enable);
+	ssize_t (*q_len_get)(struct qb_ipc_one_way *one_way);
+};
+
+struct qb_ipcs_service {
+	enum qb_ipc_type type;
+	char name[NAME_MAX];
+	uint32_t max_buffer_size;
+	int32_t service_id;
+	int32_t ref_count;
+	pid_t pid;
+	int32_t needs_sock_for_poll;
+	int32_t server_sock;
+
+	struct qb_ipcs_service_handlers serv_fns;
+	struct qb_ipcs_poll_handlers poll_fns;
+	struct qb_ipcs_funcs funcs;
+	enum qb_loop_priority poll_priority;
+
+	struct qb_list_head connections;
+	struct qb_list_head list;
+	struct qb_ipcs_stats stats;
+
+	void *context;
+};
+
+enum qb_ipcs_connection_state {
+	QB_IPCS_CONNECTION_INACTIVE,
+	QB_IPCS_CONNECTION_ACTIVE,
+	QB_IPCS_CONNECTION_ESTABLISHED,
+	QB_IPCS_CONNECTION_SHUTTING_DOWN,
+};
+
+#define CONNECTION_DESCRIPTION (16)
+
+struct qb_ipcs_connection_auth {
+	uid_t uid;
+	gid_t gid;
+	mode_t mode;
+};
+
+struct qb_ipcs_connection {
+	enum qb_ipcs_connection_state state;
+	int32_t refcount;
+	pid_t pid;
+	uid_t euid;
+	gid_t egid;
+	struct qb_ipcs_connection_auth auth;
+	struct qb_ipc_one_way setup;
+	struct qb_ipc_one_way request;
+	struct qb_ipc_one_way response;
+	struct qb_ipc_one_way event;
+	struct qb_ipcs_service *service;
+	struct qb_list_head list;
+	struct qb_ipc_request_header *receive_buf;
+	void *context;
+	int32_t fc_enabled;
+	int32_t poll_events;
+	int32_t outstanding_notifiers;
+	char description[CONNECTION_DESCRIPTION];
+	struct qb_ipcs_connection_stats_2 stats;
+};
+
+void qb_ipcs_us_init(struct qb_ipcs_service *s);
+void qb_ipcs_shm_init(struct qb_ipcs_service *s);
+
+int32_t qb_ipcs_us_publish(struct qb_ipcs_service *s);
+int32_t qb_ipcs_us_withdraw(struct qb_ipcs_service *s);
+int32_t qb_ipcc_us_sock_connect(const char *socket_name, int32_t * sock_pt);
+
+int32_t qb_ipcs_dispatch_connection_request(int32_t fd, int32_t revents, void *data);
+struct qb_ipcs_connection* qb_ipcs_connection_alloc(struct qb_ipcs_service *s);
+
+int32_t qb_ipcs_process_request(struct qb_ipcs_service *s,
+	struct qb_ipc_request_header *hdr);
+
+int32_t qb_ipc_us_sock_error_is_disconnected(int err);
+
+#endif /* QB_IPC_INT_H_DEFINED */
diff --git a/lib/ipc_setup.c b/lib/ipc_setup.c
new file mode 100644
index 0000000..1ea085b
--- /dev/null
+++ b/lib/ipc_setup.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2010,2013 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#if defined(HAVE_GETPEERUCRED)
+#include <ucred.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif /* HAVE_SYS_UN_H */
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <qb/qbatomic.h>
+#include <qb/qbipcs.h>
+#include <qb/qbloop.h>
+#include <qb/qbdefs.h>
+
+#include "util_int.h"
+#include "ipc_int.h"
+
+struct ipc_auth_ugp {
+	uid_t uid;
+	gid_t gid;
+	pid_t pid;
+};
+
+static int32_t qb_ipcs_us_connection_acceptor(int fd, int revent, void *data);
+
+ssize_t
+qb_ipc_us_send(struct qb_ipc_one_way *one_way, const void *msg, size_t len)
+{
+	int32_t result;
+	int32_t processed = 0;
+	char *rbuf = (char *)msg;
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+
+retry_send:
+	result = send(one_way->u.us.sock,
+		      &rbuf[processed], len - processed, MSG_NOSIGNAL);
+
+	if (result == -1) {
+		if (errno == EAGAIN && processed > 0) {
+			goto retry_send;
+		} else {
+			qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+			return -errno;
+		}
+	}
+
+	processed += result;
+	if (processed != len) {
+		goto retry_send;
+	}
+
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+
+	return processed;
+}
+
+static ssize_t
+qb_ipc_us_recv_msghdr(int32_t s, struct msghdr *hdr, char *msg, size_t len)
+{
+	int32_t result;
+	int32_t processed = 0;
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+
+retry_recv:
+	hdr->msg_iov->iov_base = &msg[processed];
+	hdr->msg_iov->iov_len = len - processed;
+
+	result = recvmsg(s, hdr, MSG_NOSIGNAL | MSG_WAITALL);
+	if (result == -1 && errno == EAGAIN) {
+		goto retry_recv;
+	}
+	if (result == -1) {
+		qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+		return -errno;
+	}
+	if (result == 0) {
+		qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+		qb_util_log(LOG_DEBUG,
+			    "recv(fd %d) got 0 bytes assuming ENOTCONN", s);
+		return -ENOTCONN;
+	}
+
+	processed += result;
+	if (processed != len) {
+		goto retry_recv;
+	}
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+	assert(processed == len);
+
+	return processed;
+}
+
+int32_t
+qb_ipc_us_sock_error_is_disconnected(int err)
+{
+	if (err >= 0) {
+		return QB_FALSE;
+	} else if (err == -EAGAIN ||
+	    err == -ETIMEDOUT ||
+	    err == -EINTR ||
+#ifdef EWOULDBLOCK
+	    err == -EWOULDBLOCK ||
+#endif
+	    err == -EMSGSIZE ||
+	    err == -ENOMSG ||
+	    err == -EINVAL) {
+		return QB_FALSE;
+	}
+	return QB_TRUE;
+}
+
+int32_t
+qb_ipc_us_ready(struct qb_ipc_one_way * ow_data,
+		struct qb_ipc_one_way * ow_conn,
+		int32_t ms_timeout, int32_t events)
+{
+	struct pollfd ufds[2];
+	int32_t poll_events;
+	int numfds = 1;
+	int i;
+
+	ufds[0].fd = ow_data->u.us.sock;
+	ufds[0].events = events;
+	ufds[0].revents = 0;
+
+	if (ow_conn && ow_data != ow_conn) {
+		numfds++;
+		ufds[1].fd = ow_conn->u.us.sock;
+		ufds[1].events = POLLIN;
+		ufds[1].revents = 0;
+	}
+	poll_events = poll(ufds, numfds, ms_timeout);
+	if ((poll_events == -1 && errno == EINTR) || poll_events == 0) {
+		return -EAGAIN;
+	} else if (poll_events == -1) {
+		return -errno;
+	}
+	for (i = 0; i < poll_events; i++) {
+		if (ufds[i].revents & POLLERR) {
+			qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLERR",
+				    ufds[i].fd);
+			return -ENOTCONN;
+		} else if (ufds[i].revents & POLLHUP) {
+			qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLHUP",
+				    ufds[i].fd);
+			return -ENOTCONN;
+		} else if (ufds[i].revents & POLLNVAL) {
+			qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLNVAL",
+				    ufds[i].fd);
+			return -ENOTCONN;
+		} else if (ufds[i].revents == 0) {
+			qb_util_log(LOG_DEBUG, "poll(fd %d) zero revents",
+				    ufds[i].fd);
+			return -ENOTCONN;
+		}
+	}
+	return 0;
+}
+
+/*
+ * recv an entire message - and try hard to get all of it.
+ */
+ssize_t
+qb_ipc_us_recv(struct qb_ipc_one_way * one_way,
+	       void *msg, size_t len, int32_t timeout)
+{
+	int32_t result;
+	int32_t final_rc = 0;
+	int32_t processed = 0;
+	int32_t to_recv = len;
+	char *data = msg;
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+
+retry_recv:
+	result = recv(one_way->u.us.sock, &data[processed], to_recv,
+		      MSG_NOSIGNAL | MSG_WAITALL);
+
+	if (result == -1) {
+		if (errno == EAGAIN && (processed > 0 || timeout == -1)) {
+			result = qb_ipc_us_ready(one_way, NULL, timeout, POLLIN);
+			if (result == 0 || result == -EAGAIN) {
+				goto retry_recv;
+			}
+			final_rc = result;
+			goto cleanup_sigpipe;
+		} else if (errno == ECONNRESET || errno == EPIPE) {
+			final_rc = -ENOTCONN;
+			goto cleanup_sigpipe;
+		} else {
+			final_rc = -errno;
+			goto cleanup_sigpipe;
+		}
+	}
+
+	if (result == 0) {
+		final_rc = -ENOTCONN;
+		goto cleanup_sigpipe;
+	}
+	processed += result;
+	to_recv -= result;
+	if (processed != len) {
+		goto retry_recv;
+	}
+	final_rc = processed;
+
+cleanup_sigpipe:
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+	return final_rc;
+}
+
+static int32_t
+qb_ipcc_stream_sock_connect(const char *socket_name, int32_t * sock_pt)
+{
+	int32_t request_fd;
+	struct sockaddr_un address;
+	int32_t res = 0;
+
+	request_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (request_fd == -1) {
+		return -errno;
+	}
+
+	qb_socket_nosigpipe(request_fd);
+
+	res = qb_sys_fd_nonblock_cloexec_set(request_fd);
+	if (res < 0) {
+		goto error_connect;
+	}
+
+	memset(&address, 0, sizeof(struct sockaddr_un));
+	address.sun_family = AF_UNIX;
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+	address.sun_len = QB_SUN_LEN(&address);
+#endif
+
+#if defined(QB_LINUX) || defined(QB_CYGWIN)
+	snprintf(address.sun_path + 1, UNIX_PATH_MAX - 1, "%s", socket_name);
+#else
+	snprintf(address.sun_path, sizeof(address.sun_path), "%s/%s", SOCKETDIR,
+		 socket_name);
+#endif
+	if (connect(request_fd, (struct sockaddr *)&address,
+		    QB_SUN_LEN(&address)) == -1) {
+		res = -errno;
+		goto error_connect;
+	}
+
+	*sock_pt = request_fd;
+	return 0;
+
+error_connect:
+	close(request_fd);
+	*sock_pt = -1;
+
+	return res;
+}
+
+void
+qb_ipcc_us_sock_close(int32_t sock)
+{
+	shutdown(sock, SHUT_RDWR);
+	close(sock);
+}
+
+int32_t
+qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,
+			 struct qb_ipc_connection_response *r)
+{
+	int32_t res;
+	struct qb_ipc_connection_request request;
+#ifdef QB_LINUX
+	int off = 0;
+	int on = 1;
+#endif
+
+	res = qb_ipcc_stream_sock_connect(c->name, &c->setup.u.us.sock);
+	if (res != 0) {
+		return res;
+	}
+#ifdef QB_LINUX
+	setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &on,
+		   sizeof(on));
+#endif
+
+	memset(&request, 0, sizeof(request));
+	request.hdr.id = QB_IPC_MSG_AUTHENTICATE;
+	request.hdr.size = sizeof(request);
+	request.max_msg_size = c->setup.max_msg_size;
+	res = qb_ipc_us_send(&c->setup, &request, request.hdr.size);
+	if (res < 0) {
+		qb_ipcc_us_sock_close(c->setup.u.us.sock);
+		return res;
+	}
+#ifdef QB_LINUX
+	setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &off,
+		   sizeof(off));
+#endif
+
+	res =
+	    qb_ipc_us_recv(&c->setup, r,
+			   sizeof(struct qb_ipc_connection_response), -1);
+	if (res < 0) {
+		return res;
+	}
+
+	if (r->hdr.error != 0) {
+		return r->hdr.error;
+	}
+	return 0;
+}
+
+/*
+ **************************************************************************
+ * SERVER
+ */
+
+int32_t
+qb_ipcs_us_publish(struct qb_ipcs_service * s)
+{
+	struct sockaddr_un un_addr;
+	int32_t res;
+#ifdef SO_PASSCRED
+	int on = 1;
+#endif
+
+	/*
+	 * Create socket for IPC clients, name socket, listen for connections
+	 */
+	s->server_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (s->server_sock == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "Cannot create server socket");
+		return res;
+	}
+
+	res = qb_sys_fd_nonblock_cloexec_set(s->server_sock);
+	if (res < 0) {
+		goto error_close;
+	}
+
+	memset(&un_addr, 0, sizeof(struct sockaddr_un));
+	un_addr.sun_family = AF_UNIX;
+#if defined(QB_BSD) || defined(QB_DARWIN)
+	un_addr.sun_len = SUN_LEN(&un_addr);
+#endif
+
+	qb_util_log(LOG_INFO, "server name: %s", s->name);
+#if defined(QB_LINUX) || defined(QB_CYGWIN)
+	snprintf(un_addr.sun_path + 1, UNIX_PATH_MAX - 1, "%s", s->name);
+#else
+	{
+		struct stat stat_out;
+		res = stat(SOCKETDIR, &stat_out);
+		if (res == -1 || (res == 0 && !S_ISDIR(stat_out.st_mode))) {
+			res = -errno;
+			qb_util_log(LOG_CRIT,
+				    "Required directory not present %s",
+				    SOCKETDIR);
+			goto error_close;
+		}
+		snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/%s", SOCKETDIR,
+			 s->name);
+		unlink(un_addr.sun_path);
+	}
+#endif
+
+	res = bind(s->server_sock, (struct sockaddr *)&un_addr,
+		   QB_SUN_LEN(&un_addr));
+	if (res) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "Could not bind AF_UNIX (%s)",
+			       un_addr.sun_path);
+		goto error_close;
+	}
+
+	/*
+	 * Allow everyone to write to the socket since the IPC layer handles
+	 * security automatically
+	 */
+#if !defined(QB_LINUX) && !defined(QB_CYGWIN)
+	res = chmod(un_addr.sun_path, S_IRWXU | S_IRWXG | S_IRWXO);
+#endif
+#ifdef SO_PASSCRED
+	setsockopt(s->server_sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+#endif
+	if (listen(s->server_sock, SERVER_BACKLOG) == -1) {
+		qb_util_perror(LOG_ERR, "socket listen failed");
+	}
+
+	res = s->poll_fns.dispatch_add(s->poll_priority, s->server_sock,
+				       POLLIN | POLLPRI | POLLNVAL,
+				       s, qb_ipcs_us_connection_acceptor);
+	return res;
+
+error_close:
+	close(s->server_sock);
+	return res;
+}
+
+int32_t
+qb_ipcs_us_withdraw(struct qb_ipcs_service * s)
+{
+	qb_util_log(LOG_INFO, "withdrawing server sockets");
+	(void)s->poll_fns.dispatch_del(s->server_sock);
+	shutdown(s->server_sock, SHUT_RDWR);
+	close(s->server_sock);
+	return 0;
+}
+
+static int32_t
+handle_new_connection(struct qb_ipcs_service *s,
+		      int32_t auth_result,
+		      int32_t sock,
+		      void *msg, size_t len, struct ipc_auth_ugp *ugp)
+{
+	struct qb_ipcs_connection *c = NULL;
+	struct qb_ipc_connection_request *req = msg;
+	int32_t res = auth_result;
+	int32_t res2 = 0;
+	uint32_t max_buffer_size = QB_MAX(req->max_msg_size, s->max_buffer_size);
+	struct qb_ipc_connection_response response;
+
+	c = qb_ipcs_connection_alloc(s);
+	if (c == NULL) {
+		qb_ipcc_us_sock_close(sock);
+		return -ENOMEM;
+	}
+
+	c->receive_buf = calloc(1, max_buffer_size);
+	if (c->receive_buf == NULL) {
+		free(c);
+		qb_ipcc_us_sock_close(sock);
+		return -ENOMEM;
+	}
+	c->setup.u.us.sock = sock;
+	c->request.max_msg_size = max_buffer_size;
+	c->response.max_msg_size = max_buffer_size;
+	c->event.max_msg_size = max_buffer_size;
+	c->pid = ugp->pid;
+	c->auth.uid = c->euid = ugp->uid;
+	c->auth.gid = c->egid = ugp->gid;
+	c->auth.mode = 0600;
+	c->stats.client_pid = ugp->pid;
+	snprintf(c->description, CONNECTION_DESCRIPTION,
+		 "%d-%d-%d", s->pid, ugp->pid, c->setup.u.us.sock);
+
+	if (auth_result == 0 && c->service->serv_fns.connection_accept) {
+		res = c->service->serv_fns.connection_accept(c,
+							     c->euid, c->egid);
+	}
+	if (res != 0) {
+		goto send_response;
+	}
+
+	qb_util_log(LOG_DEBUG, "IPC credentials authenticated (%s)",
+		    c->description);
+
+	memset(&response, 0, sizeof(response));
+	if (s->funcs.connect) {
+		res = s->funcs.connect(s, c, &response);
+		if (res != 0) {
+			goto send_response;
+		}
+	}
+	/*
+	 * The connection is good, add it to the active connection list
+	 */
+	c->state = QB_IPCS_CONNECTION_ACTIVE;
+	qb_list_add(&c->list, &s->connections);
+
+send_response:
+	response.hdr.id = QB_IPC_MSG_AUTHENTICATE;
+	response.hdr.size = sizeof(response);
+	response.hdr.error = res;
+	if (res == 0) {
+		response.connection = (intptr_t) c;
+		response.connection_type = s->type;
+		response.max_msg_size = c->request.max_msg_size;
+		s->stats.active_connections++;
+	}
+
+	res2 = qb_ipc_us_send(&c->setup, &response, response.hdr.size);
+	if (res == 0 && res2 != response.hdr.size) {
+		res = res2;
+	}
+
+	if (res == 0) {
+		qb_ipcs_connection_ref(c);
+		if (s->serv_fns.connection_created) {
+			s->serv_fns.connection_created(c);
+		}
+		if (c->state == QB_IPCS_CONNECTION_ACTIVE) {
+			c->state = QB_IPCS_CONNECTION_ESTABLISHED;
+		}
+		qb_ipcs_connection_unref(c);
+	} else {
+		if (res == -EACCES) {
+			qb_util_log(LOG_ERR, "Invalid IPC credentials (%s).",
+				    c->description);
+		} else if (res == -EAGAIN) {
+			qb_util_log(LOG_WARNING, "Denied connection, is not ready (%s)",
+				    c->description);
+		} else {
+			errno = -res;
+			qb_util_perror(LOG_ERR,
+				       "Error in connection setup (%s)",
+				       c->description);
+		}
+		qb_ipcs_disconnect(c);
+	}
+	return res;
+}
+
+static int32_t
+qb_ipcs_uc_recv_and_auth(int32_t sock, void *msg, size_t len,
+			 struct ipc_auth_ugp *ugp)
+{
+	int32_t res = 0;
+	struct msghdr msg_recv;
+	struct iovec iov_recv;
+
+#ifdef SO_PASSCRED
+	char cmsg_cred[CMSG_SPACE(sizeof(struct ucred))];
+	int off = 0;
+	int on = 1;
+#endif
+	msg_recv.msg_iov = &iov_recv;
+	msg_recv.msg_iovlen = 1;
+	msg_recv.msg_name = 0;
+	msg_recv.msg_namelen = 0;
+#ifdef SO_PASSCRED
+	msg_recv.msg_control = (void *)cmsg_cred;
+	msg_recv.msg_controllen = sizeof(cmsg_cred);
+#endif
+#ifdef QB_SOLARIS
+	msg_recv.msg_accrights = 0;
+	msg_recv.msg_accrightslen = 0;
+#else
+	msg_recv.msg_flags = 0;
+#endif /* QB_SOLARIS */
+
+	iov_recv.iov_base = msg;
+	iov_recv.iov_len = len;
+#ifdef SO_PASSCRED
+	setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+#endif
+
+	res = qb_ipc_us_recv_msghdr(sock, &msg_recv, msg, len);
+	if (res < 0) {
+		goto cleanup_and_return;
+	}
+	if (res != len) {
+		res = -EIO;
+		goto cleanup_and_return;
+	}
+
+	/*
+	 * currently support getpeerucred, getpeereid, and SO_PASSCRED credential
+	 * retrieval mechanisms for various Platforms
+	 */
+#ifdef HAVE_GETPEERUCRED
+	/*
+	 * Solaris and some BSD systems
+	 */
+	{
+		ucred_t *uc = NULL;
+
+		if (getpeerucred(sock, &uc) == 0) {
+			res = 0;
+			ugp->uid = ucred_geteuid(uc);
+			ugp->gid = ucred_getegid(uc);
+			ugp->pid = ucred_getpid(uc);
+			ucred_free(uc);
+		} else {
+			res = -errno;
+		}
+	}
+#elif HAVE_GETPEEREID
+	/*
+	 * Usually MacOSX systems
+	 */
+	{
+		/*
+		 * TODO get the peer's pid.
+		 * c->pid = ?;
+		 */
+		if (getpeereid(sock, &ugp->uid, &ugp->gid) == 0) {
+			res = 0;
+		} else {
+			res = -errno;
+		}
+	}
+
+#elif SO_PASSCRED
+	/*
+	 * Usually Linux systems
+	 */
+	{
+		struct ucred cred;
+		struct cmsghdr *cmsg;
+
+		res = -EINVAL;
+		for (cmsg = CMSG_FIRSTHDR(&msg_recv); cmsg != NULL;
+		     cmsg = CMSG_NXTHDR(&msg_recv, cmsg)) {
+			if (cmsg->cmsg_type != SCM_CREDENTIALS)
+				continue;
+
+			memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
+			res = 0;
+			ugp->pid = cred.pid;
+			ugp->uid = cred.uid;
+			ugp->gid = cred.gid;
+			break;
+		}
+	}
+#else /* no credentials */
+	ugp->pid = 0;
+	ugp->uid = 0;
+	ugp->gid = 0;
+	res = -ENOTSUP;
+#endif /* no credentials */
+
+cleanup_and_return:
+
+#ifdef SO_PASSCRED
+	setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &off, sizeof(off));
+#endif
+
+	return res;
+}
+
+static int32_t
+qb_ipcs_us_connection_acceptor(int fd, int revent, void *data)
+{
+	struct sockaddr_un un_addr;
+	int32_t new_fd;
+	struct qb_ipcs_service *s = (struct qb_ipcs_service *)data;
+	int32_t res;
+	struct qb_ipc_connection_request setup_msg;
+	struct ipc_auth_ugp ugp;
+	socklen_t addrlen = sizeof(struct sockaddr_un);
+
+	if (revent & (POLLNVAL | POLLHUP | POLLERR)) {
+		/*
+		 * handle shutdown more cleanly.
+		 */
+		return -1;
+	}
+
+retry_accept:
+	errno = 0;
+	new_fd = accept(fd, (struct sockaddr *)&un_addr, &addrlen);
+	if (new_fd == -1 && errno == EINTR) {
+		goto retry_accept;
+	}
+
+	if (new_fd == -1 && errno == EBADF) {
+		qb_util_perror(LOG_ERR,
+			       "Could not accept client connection from fd:%d",
+			       fd);
+		return -1;
+	}
+	if (new_fd == -1) {
+		qb_util_perror(LOG_ERR, "Could not accept client connection");
+		/* This is an error, but -1 would indicate disconnect
+		 * from the poll loop
+		 */
+		return 0;
+	}
+
+	res = qb_sys_fd_nonblock_cloexec_set(new_fd);
+	if (res < 0) {
+		close(new_fd);
+		/* This is an error, but -1 would indicate disconnect
+		 * from the poll loop
+		 */
+		return 0;
+	}
+
+	res = qb_ipcs_uc_recv_and_auth(new_fd, &setup_msg, sizeof(setup_msg),
+				       &ugp);
+	if (res < 0) {
+		close(new_fd);
+		/* This is an error, but -1 would indicate disconnect
+		 * from the poll loop
+		 */
+		return 0;
+	}
+
+	if (setup_msg.hdr.id == QB_IPC_MSG_AUTHENTICATE) {
+		(void)handle_new_connection(s, res, new_fd, &setup_msg,
+					    sizeof(setup_msg), &ugp);
+	} else {
+		close(new_fd);
+	}
+
+	return 0;
+}
diff --git a/lib/ipc_shm.c b/lib/ipc_shm.c
new file mode 100644
index 0000000..5eaf245
--- /dev/null
+++ b/lib/ipc_shm.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include "ipc_int.h"
+#include "util_int.h"
+#include "ringbuffer_int.h"
+#include <qb/qbdefs.h>
+#include <qb/qbatomic.h>
+#include <qb/qbloop.h>
+#include <qb/qbrb.h>
+
+/*
+ * utility functions
+ * --------------------------------------------------------
+ */
+/*
+ * client functions
+ * --------------------------------------------------------
+ */
+static void
+qb_ipcc_shm_disconnect(struct qb_ipcc_connection *c)
+{
+	qb_ipcc_us_sock_close(c->setup.u.us.sock);
+	if (c->is_connected) {
+		qb_rb_close(c->request.u.shm.rb);
+		qb_rb_close(c->response.u.shm.rb);
+		qb_rb_close(c->event.u.shm.rb);
+	} else {
+		qb_rb_force_close(c->request.u.shm.rb);
+		qb_rb_force_close(c->response.u.shm.rb);
+		qb_rb_force_close(c->event.u.shm.rb);
+	}
+}
+
+static ssize_t
+qb_ipc_shm_send(struct qb_ipc_one_way *one_way,
+		const void *msg_ptr, size_t msg_len)
+{
+	return qb_rb_chunk_write(one_way->u.shm.rb, msg_ptr, msg_len);
+}
+
+static ssize_t
+qb_ipc_shm_sendv(struct qb_ipc_one_way *one_way,
+		 const struct iovec *iov, size_t iov_len)
+{
+	char *dest;
+	int32_t res = 0;
+	int32_t total_size = 0;
+	int32_t i;
+	char *pt = NULL;
+
+	if (one_way->u.shm.rb == NULL) {
+		return -ENOTCONN;
+	}
+
+	for (i = 0; i < iov_len; i++) {
+		total_size += iov[i].iov_len;
+	}
+	dest = qb_rb_chunk_alloc(one_way->u.shm.rb, total_size);
+	if (dest == NULL) {
+		return -errno;
+	}
+	pt = dest;
+
+	for (i = 0; i < iov_len; i++) {
+		memcpy(pt, iov[i].iov_base, iov[i].iov_len);
+		pt += iov[i].iov_len;
+	}
+	res = qb_rb_chunk_commit(one_way->u.shm.rb, total_size);
+	if (res < 0) {
+		return res;
+	}
+	return total_size;
+}
+
+static ssize_t
+qb_ipc_shm_recv(struct qb_ipc_one_way *one_way,
+		void *msg_ptr, size_t msg_len, int32_t ms_timeout)
+{
+	if (one_way->u.shm.rb == NULL) {
+		return -ENOTCONN;
+	}
+	return qb_rb_chunk_read(one_way->u.shm.rb,
+				(void *)msg_ptr, msg_len, ms_timeout);
+}
+
+static ssize_t
+qb_ipc_shm_peek(struct qb_ipc_one_way *one_way, void **data_out,
+		int32_t ms_timeout)
+{
+	ssize_t rc;
+	if (one_way->u.shm.rb == NULL) {
+		return -ENOTCONN;
+	}
+	rc = qb_rb_chunk_peek(one_way->u.shm.rb, data_out, ms_timeout);
+	if (rc == 0)  {
+		return -EAGAIN;
+	}
+	return rc;
+}
+
+static void
+qb_ipc_shm_reclaim(struct qb_ipc_one_way *one_way)
+{
+	if (one_way->u.shm.rb != NULL) {
+		qb_rb_chunk_reclaim(one_way->u.shm.rb);
+	}
+}
+
+static void
+qb_ipc_shm_fc_set(struct qb_ipc_one_way *one_way, int32_t fc_enable)
+{
+	int32_t *fc;
+	fc = qb_rb_shared_user_data_get(one_way->u.shm.rb);
+	qb_util_log(LOG_TRACE, "setting fc to %d", fc_enable);
+	qb_atomic_int_set(fc, fc_enable);
+}
+
+static int32_t
+qb_ipc_shm_fc_get(struct qb_ipc_one_way *one_way)
+{
+	int32_t *fc;
+	int32_t rc = qb_rb_refcount_get(one_way->u.shm.rb);
+
+	if (rc != 2) {
+		return -ENOTCONN;
+	}
+	fc = qb_rb_shared_user_data_get(one_way->u.shm.rb);
+	return qb_atomic_int_get(fc);
+}
+
+static ssize_t
+qb_ipc_shm_q_len_get(struct qb_ipc_one_way *one_way)
+{
+	if (one_way->u.shm.rb == NULL) {
+		return -ENOTCONN;
+	}
+	return qb_rb_chunks_used(one_way->u.shm.rb);
+}
+
+int32_t
+qb_ipcc_shm_connect(struct qb_ipcc_connection * c,
+		    struct qb_ipc_connection_response * response)
+{
+	int32_t res = 0;
+
+	c->funcs.send = qb_ipc_shm_send;
+	c->funcs.sendv = qb_ipc_shm_sendv;
+	c->funcs.recv = qb_ipc_shm_recv;
+	c->funcs.fc_get = qb_ipc_shm_fc_get;
+	c->funcs.disconnect = qb_ipcc_shm_disconnect;
+	c->needs_sock_for_poll = QB_TRUE;
+
+	if (strlen(c->name) > (NAME_MAX - 20)) {
+		errno = EINVAL;
+		return -errno;
+	}
+
+	c->request.u.shm.rb = qb_rb_open(response->request,
+					 c->request.max_msg_size,
+					 QB_RB_FLAG_SHARED_PROCESS,
+					 sizeof(int32_t));
+	if (c->request.u.shm.rb == NULL) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "qb_rb_open:REQUEST");
+		goto return_error;
+	}
+	c->response.u.shm.rb = qb_rb_open(response->response,
+					  c->response.max_msg_size,
+					  QB_RB_FLAG_SHARED_PROCESS, 0);
+
+	if (c->response.u.shm.rb == NULL) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "qb_rb_open:RESPONSE");
+		goto cleanup_request;
+	}
+	c->event.u.shm.rb = qb_rb_open(response->event,
+				       c->response.max_msg_size,
+				       QB_RB_FLAG_SHARED_PROCESS, 0);
+
+	if (c->event.u.shm.rb == NULL) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "qb_rb_open:EVENT");
+		goto cleanup_request_response;
+	}
+	return 0;
+
+cleanup_request_response:
+	qb_rb_close(c->response.u.shm.rb);
+
+cleanup_request:
+	qb_rb_close(c->request.u.shm.rb);
+
+return_error:
+	errno = -res;
+	qb_util_perror(LOG_ERR, "connection failed");
+
+	return res;
+}
+
+/*
+ * service functions
+ * --------------------------------------------------------
+ */
+
+static void
+qb_ipcs_shm_disconnect(struct qb_ipcs_connection *c)
+{
+	if (c->state == QB_IPCS_CONNECTION_ESTABLISHED ||
+	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
+		if (c->setup.u.us.sock > 0) {
+			qb_ipcc_us_sock_close(c->setup.u.us.sock);
+			(void)c->service->poll_fns.dispatch_del(c->setup.u.us.sock);
+			c->setup.u.us.sock = -1;
+		}
+	}
+	if (c->state == QB_IPCS_CONNECTION_SHUTTING_DOWN ||
+	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
+		if (c->response.u.shm.rb) {
+			qb_rb_close(c->response.u.shm.rb);
+			c->response.u.shm.rb = NULL;
+		}
+		if (c->event.u.shm.rb) {
+			qb_rb_close(c->event.u.shm.rb);
+			c->event.u.shm.rb = NULL;
+		}
+		if (c->request.u.shm.rb) {
+			qb_rb_close(c->request.u.shm.rb);
+			c->request.u.shm.rb = NULL;
+		}
+	}
+}
+
+static int32_t
+qb_ipcs_shm_rb_open(struct qb_ipcs_connection *c,
+		    struct qb_ipc_one_way *ow,
+		    const char *rb_name)
+{
+	int32_t res = 0;
+
+	ow->u.shm.rb = qb_rb_open(rb_name,
+				  ow->max_msg_size,
+				  QB_RB_FLAG_CREATE |
+				  QB_RB_FLAG_SHARED_PROCESS,
+				  sizeof(int32_t));
+	if (ow->u.shm.rb == NULL) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "qb_rb_open:%s", rb_name);
+		return res;
+	}
+	res = qb_rb_chown(ow->u.shm.rb, c->auth.uid, c->auth.gid);
+	if (res != 0) {
+		qb_util_perror(LOG_ERR, "qb_rb_chown:%s", rb_name);
+		goto cleanup;
+	}
+	res = qb_rb_chmod(ow->u.shm.rb, c->auth.mode);
+	if (res != 0) {
+		qb_util_perror(LOG_ERR, "qb_rb_chmod:%s", rb_name);
+		goto cleanup;
+	}
+	return res;
+
+cleanup:
+	qb_rb_close(ow->u.shm.rb);
+	return res;
+}
+
+static int32_t
+qb_ipcs_shm_connect(struct qb_ipcs_service *s,
+		    struct qb_ipcs_connection *c,
+		    struct qb_ipc_connection_response *r)
+{
+	int32_t res;
+
+	qb_util_log(LOG_DEBUG, "connecting to client [%d]", c->pid);
+
+	snprintf(r->request, NAME_MAX, "%s-request-%s",
+		 s->name, c->description);
+	snprintf(r->response, NAME_MAX, "%s-response-%s",
+		 s->name, c->description);
+	snprintf(r->event, NAME_MAX, "%s-event-%s",
+		 s->name, c->description);
+
+	res = qb_ipcs_shm_rb_open(c, &c->request,
+				  r->request);
+	if (res != 0) {
+		goto cleanup;
+	}
+
+	res = qb_ipcs_shm_rb_open(c, &c->response,
+				  r->response);
+	if (res != 0) {
+		goto cleanup_request;
+	}
+
+	res = qb_ipcs_shm_rb_open(c, &c->event,
+				  r->event);
+	if (res != 0) {
+		goto cleanup_request_response;
+	}
+
+	res = s->poll_fns.dispatch_add(s->poll_priority,
+				       c->setup.u.us.sock,
+				       POLLIN | POLLPRI | POLLNVAL,
+				       c, qb_ipcs_dispatch_connection_request);
+	if (res != 0) {
+		qb_util_log(LOG_ERR,
+			    "Error adding socket to mainloop (%s).",
+			    c->description);
+		goto cleanup_request_response_event;
+	}
+
+	r->hdr.error = 0;
+	return 0;
+
+cleanup_request_response_event:
+	qb_rb_close(c->event.u.shm.rb);
+
+cleanup_request_response:
+	qb_rb_close(c->response.u.shm.rb);
+
+cleanup_request:
+	qb_rb_close(c->request.u.shm.rb);
+
+cleanup:
+	r->hdr.error = res;
+	errno = -res;
+	qb_util_perror(LOG_ERR, "shm connection FAILED");
+
+	return res;
+}
+
+void
+qb_ipcs_shm_init(struct qb_ipcs_service *s)
+{
+	s->funcs.connect = qb_ipcs_shm_connect;
+	s->funcs.disconnect = qb_ipcs_shm_disconnect;
+
+	s->funcs.recv = qb_ipc_shm_recv;
+	s->funcs.peek = qb_ipc_shm_peek;
+	s->funcs.reclaim = qb_ipc_shm_reclaim;
+	s->funcs.send = qb_ipc_shm_send;
+	s->funcs.sendv = qb_ipc_shm_sendv;
+
+	s->funcs.fc_set = qb_ipc_shm_fc_set;
+	s->funcs.q_len_get = qb_ipc_shm_q_len_get;
+
+	s->needs_sock_for_poll = QB_TRUE;
+}
diff --git a/lib/ipc_socket.c b/lib/ipc_socket.c
new file mode 100644
index 0000000..8fab48f
--- /dev/null
+++ b/lib/ipc_socket.c
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2010,2013 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif /* HAVE_SYS_UN_H */
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <qb/qbatomic.h>
+#include <qb/qbipcs.h>
+#include <qb/qbloop.h>
+#include <qb/qbdefs.h>
+
+#include "util_int.h"
+#include "ipc_int.h"
+
+struct ipc_us_control {
+	int32_t sent;
+	int32_t flow_control;
+};
+#define SHM_CONTROL_SIZE (3 * sizeof(struct ipc_us_control))
+
+static void
+set_sock_addr(struct sockaddr_un *address, const char *socket_name)
+{
+	memset(address, 0, sizeof(struct sockaddr_un));
+	address->sun_family = AF_UNIX;
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+	address->sun_len = QB_SUN_LEN(address);
+#endif
+
+#if defined(QB_LINUX) || defined(QB_CYGWIN)
+	snprintf(address->sun_path + 1, UNIX_PATH_MAX - 1, "%s", socket_name);
+#else
+	snprintf(address->sun_path, sizeof(address->sun_path), "%s/%s", SOCKETDIR,
+		 socket_name);
+#endif
+}
+
+static int32_t
+qb_ipc_dgram_sock_setup(const char *base_name,
+			const char *service_name, int32_t * sock_pt)
+{
+	int32_t request_fd;
+	struct sockaddr_un local_address;
+	int32_t res = 0;
+	char sock_path[PATH_MAX];
+
+	request_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (request_fd == -1) {
+		return -errno;
+	}
+
+	qb_socket_nosigpipe(request_fd);
+	res = qb_sys_fd_nonblock_cloexec_set(request_fd);
+	if (res < 0) {
+		goto error_connect;
+	}
+	snprintf(sock_path, PATH_MAX, "%s-%s", base_name, service_name);
+	set_sock_addr(&local_address, sock_path);
+#if !(defined(QB_LINUX) || defined(QB_CYGWIN))
+	res = unlink(local_address.sun_path);
+#endif
+	res = bind(request_fd, (struct sockaddr *)&local_address,
+		   sizeof(local_address));
+	if (res < 0) {
+		goto error_connect;
+	}
+
+	*sock_pt = request_fd;
+	return 0;
+
+error_connect:
+	close(request_fd);
+	*sock_pt = -1;
+
+	return res;
+}
+
+static int32_t
+set_sock_size(int sockfd, size_t max_msg_size)
+{
+	int32_t rc;
+	unsigned int optval;
+	socklen_t optlen = sizeof(optval);
+
+	rc = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen);
+
+	qb_util_log(LOG_TRACE, "%d: getsockopt(%d, needed:%d) actual:%d",
+		rc, sockfd, max_msg_size, optval);
+
+	/* The optvat <= max_msg_size check is weird...
+	 * during testing it was discovered in some instances if the
+	 * default optval is exactly equal to our max_msg_size, we couldn't
+	 * actually send a message that large unless we explicilty set
+	 * it using setsockopt... there is no good explaination for this. Most
+	 * likely this is hitting some sort of "off by one" error in the kernel. */
+	if (rc == 0 && optval <= max_msg_size) {
+		optval = max_msg_size;
+		optlen = sizeof(optval);
+		rc = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
+	}
+	return rc;
+}
+
+static int32_t
+dgram_verify_msg_size(size_t max_msg_size)
+{
+	int32_t rc = -1;
+	int32_t sockets[2];
+	int32_t tries = 0;
+	int32_t write_passed = 0;
+	int32_t read_passed = 0;
+	char buf[max_msg_size];
+
+	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
+		goto cleanup_socks;
+	}
+
+	if (set_sock_size(sockets[0], max_msg_size) != 0) {
+		goto cleanup_socks;
+	}
+	if (set_sock_size(sockets[1], max_msg_size) != 0) {
+		goto cleanup_socks;
+	}
+
+	for (tries = 0; tries < 3; tries++) {
+
+		if (write_passed == 0) {
+			rc = write(sockets[1], buf, max_msg_size);
+
+			if (rc < 0 && (errno == EAGAIN || errno == EINTR)) {
+				continue;
+			} else if (rc == max_msg_size) {
+				write_passed = 1;
+			} else {
+				break;
+			}
+		}
+
+		if (read_passed == 0) {
+			rc = read(sockets[0], buf, max_msg_size);
+
+			if (rc < 0 && (errno == EAGAIN || errno == EINTR)) {
+				continue;
+			} else if (rc == max_msg_size) {
+				read_passed = 1;
+			} else {
+				break;
+			}
+		}
+
+		if (read_passed && write_passed) {
+			rc = 0;
+			break;
+		}
+	}
+
+
+cleanup_socks:
+	close(sockets[0]);
+	close(sockets[1]);
+	return rc;
+}
+
+int32_t
+qb_ipcc_verify_dgram_max_msg_size(size_t max_msg_size)
+{
+	int32_t i;
+	int32_t last = -1;
+	int32_t inc = 2048;
+
+	if (dgram_verify_msg_size(max_msg_size) == 0) {
+		return max_msg_size;
+	}
+
+	for (i = inc; i < max_msg_size; i+=inc) {
+		if (dgram_verify_msg_size(i) == 0) {
+			last = i;
+		} else if (inc >= 512) {
+			i-=inc;
+			inc = inc/2;
+		} else {
+			break;
+		}
+	}
+
+	return last;
+}
+
+/*
+ * bind to "base_name-local_name"
+ * connect to "base_name-remote_name"
+ * output sock_pt
+ */
+static int32_t
+qb_ipc_dgram_sock_connect(const char *base_name,
+			  const char *local_name,
+			  const char *remote_name,
+			  int32_t max_msg_size, int32_t * sock_pt)
+{
+	char sock_path[PATH_MAX];
+	struct sockaddr_un remote_address;
+	int32_t res = qb_ipc_dgram_sock_setup(base_name, local_name,
+					      sock_pt);
+	if (res < 0) {
+		return res;
+	}
+
+	snprintf(sock_path, PATH_MAX, "%s-%s", base_name, remote_name);
+	set_sock_addr(&remote_address, sock_path);
+	if (connect(*sock_pt, (struct sockaddr *)&remote_address,
+		    QB_SUN_LEN(&remote_address)) == -1) {
+		res = -errno;
+		goto error_connect;
+	}
+
+	return set_sock_size(*sock_pt, max_msg_size);
+
+error_connect:
+	close(*sock_pt);
+	*sock_pt = -1;
+
+	return res;
+}
+
+static int32_t
+_finish_connecting(struct qb_ipc_one_way *one_way)
+{
+	struct sockaddr_un remote_address;
+	int res;
+	int error;
+	int retry = 0;
+
+	set_sock_addr(&remote_address, one_way->u.us.sock_name);
+
+	/* this retry loop is here to help connecting when trying to send
+	 * an event right after connection setup.
+	 */
+	do {
+		errno = 0;
+		res = connect(one_way->u.us.sock,
+			      (struct sockaddr *)&remote_address,
+			      QB_SUN_LEN(&remote_address));
+		if (res == -1) {
+			error = -errno;
+			qb_util_perror(LOG_DEBUG, "error calling connect()");
+			retry++;
+			usleep(100000);
+		}
+	} while (res == -1 && retry < 10);
+	if (res == -1) {
+		return error;
+	}
+
+	free(one_way->u.us.sock_name);
+	one_way->u.us.sock_name = NULL;
+
+	return set_sock_size(one_way->u.us.sock, one_way->max_msg_size);
+}
+
+/*
+ * client functions
+ * --------------------------------------------------------
+ */
+static void
+qb_ipcc_us_disconnect(struct qb_ipcc_connection *c)
+{
+	munmap(c->request.u.us.shared_data, SHM_CONTROL_SIZE);
+	unlink(c->request.u.us.shared_file_name);
+	qb_ipcc_us_sock_close(c->event.u.us.sock);
+	qb_ipcc_us_sock_close(c->request.u.us.sock);
+	qb_ipcc_us_sock_close(c->setup.u.us.sock);
+}
+
+static ssize_t
+qb_ipc_socket_send(struct qb_ipc_one_way *one_way,
+		   const void *msg_ptr, size_t msg_len)
+{
+	ssize_t rc = 0;
+	struct ipc_us_control *ctl;
+	ctl = (struct ipc_us_control *)one_way->u.us.shared_data;
+
+	if (one_way->u.us.sock_name) {
+		rc = _finish_connecting(one_way);
+		if (rc < 0) {
+			qb_util_log(LOG_ERR, "socket connect-on-send");
+			return rc;
+		}
+	}
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+	rc = send(one_way->u.us.sock, msg_ptr, msg_len, MSG_NOSIGNAL);
+	if (rc == -1) {
+		rc = -errno;
+		if (errno != EAGAIN && errno != ENOBUFS) {
+			qb_util_perror(LOG_DEBUG, "socket_send:send");
+		}
+	}
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+
+	if (ctl && rc == msg_len) {
+		qb_atomic_int_inc(&ctl->sent);
+	}
+
+	return rc;
+}
+
+static ssize_t
+qb_ipc_socket_sendv(struct qb_ipc_one_way *one_way, const struct iovec *iov,
+		    size_t iov_len)
+{
+	int32_t rc;
+	struct ipc_us_control *ctl;
+	ctl = (struct ipc_us_control *)one_way->u.us.shared_data;
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+
+	if (one_way->u.us.sock_name) {
+		rc = _finish_connecting(one_way);
+		if (rc < 0) {
+			qb_util_perror(LOG_ERR, "socket connect-on-sendv");
+			qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+			return rc;
+		}
+	}
+
+	rc = writev(one_way->u.us.sock, iov, iov_len);
+
+	if (rc == -1) {
+		rc = -errno;
+		if (errno != EAGAIN && errno != ENOBUFS) {
+			qb_util_perror(LOG_DEBUG, "socket_sendv:writev %d",
+				       one_way->u.us.sock);
+		}
+	}
+
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+
+	if (ctl && rc > 0) {
+		qb_atomic_int_inc(&ctl->sent);
+	}
+	return rc;
+}
+
+/*
+ * recv a message of unknown size.
+ */
+static ssize_t
+qb_ipc_us_recv_at_most(struct qb_ipc_one_way *one_way,
+		       void *msg, size_t len, int32_t timeout)
+{
+	int32_t result;
+	int32_t final_rc = 0;
+	int32_t to_recv = 0;
+	char *data = msg;
+	struct ipc_us_control *ctl = NULL;
+	int32_t time_waited = 0;
+	int32_t time_to_wait = timeout;
+
+	if (timeout == -1) {
+		time_to_wait = 1000;
+	}
+
+	qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
+
+retry_peek:
+	result = recv(one_way->u.us.sock, data,
+		      sizeof(struct qb_ipc_request_header),
+		      MSG_NOSIGNAL | MSG_PEEK);
+
+	if (result == -1) {
+
+		if (errno != EAGAIN) {
+			final_rc = -errno;
+#if !(defined(QB_LINUX) || defined(QB_CYGWIN))
+			if (errno == ECONNRESET || errno == EPIPE) {
+				final_rc = -ENOTCONN;
+			}
+#endif
+			goto cleanup_sigpipe;
+		}
+
+		/* check to see if we have enough time left to try again */
+		if (time_waited < timeout || timeout == -1) {
+			result = qb_ipc_us_ready(one_way, NULL, time_to_wait, POLLIN);
+			if (qb_ipc_us_sock_error_is_disconnected(result)) {
+				final_rc = result;
+				goto cleanup_sigpipe;
+			}
+			time_waited += time_to_wait;
+			goto retry_peek;
+		} else if (time_waited >= timeout) {
+			final_rc = -ETIMEDOUT;
+			goto cleanup_sigpipe;
+		}
+	}
+	if (result >= sizeof(struct qb_ipc_request_header)) {
+		struct qb_ipc_request_header *hdr = NULL;
+		hdr = (struct qb_ipc_request_header *)msg;
+		to_recv = hdr->size;
+	}
+
+	result = recv(one_way->u.us.sock, data, to_recv,
+		      MSG_NOSIGNAL | MSG_WAITALL);
+	if (result == -1) {
+		final_rc = -errno;
+		goto cleanup_sigpipe;
+	} else if (result == 0) {
+		qb_util_log(LOG_DEBUG, "recv == 0 -> ENOTCONN");
+
+		final_rc = -ENOTCONN;
+		goto cleanup_sigpipe;
+	}
+
+	final_rc = result;
+
+	ctl = (struct ipc_us_control *)one_way->u.us.shared_data;
+	if (ctl) {
+		(void)qb_atomic_int_dec_and_test(&ctl->sent);
+	}
+
+cleanup_sigpipe:
+	qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
+	return final_rc;
+}
+
+static void
+qb_ipc_us_fc_set(struct qb_ipc_one_way *one_way, int32_t fc_enable)
+{
+	struct ipc_us_control *ctl =
+	    (struct ipc_us_control *)one_way->u.us.shared_data;
+
+	qb_util_log(LOG_TRACE, "setting fc to %d", fc_enable);
+	qb_atomic_int_set(&ctl->flow_control, fc_enable);
+}
+
+static int32_t
+qb_ipc_us_fc_get(struct qb_ipc_one_way *one_way)
+{
+	struct ipc_us_control *ctl =
+	    (struct ipc_us_control *)one_way->u.us.shared_data;
+
+	return qb_atomic_int_get(&ctl->flow_control);
+}
+
+static ssize_t
+qb_ipc_us_q_len_get(struct qb_ipc_one_way *one_way)
+{
+	struct ipc_us_control *ctl =
+	    (struct ipc_us_control *)one_way->u.us.shared_data;
+	return qb_atomic_int_get(&ctl->sent);
+}
+
+int32_t
+qb_ipcc_us_connect(struct qb_ipcc_connection * c,
+		   struct qb_ipc_connection_response * r)
+{
+	int32_t res;
+	char path[PATH_MAX];
+	int32_t fd_hdr;
+	char *shm_ptr;
+
+	qb_atomic_init();
+
+	c->needs_sock_for_poll = QB_FALSE;
+	c->funcs.send = qb_ipc_socket_send;
+	c->funcs.sendv = qb_ipc_socket_sendv;
+	c->funcs.recv = qb_ipc_us_recv_at_most;
+	c->funcs.fc_get = qb_ipc_us_fc_get;
+	c->funcs.disconnect = qb_ipcc_us_disconnect;
+
+	fd_hdr = qb_sys_mmap_file_open(path, r->request,
+				       SHM_CONTROL_SIZE, O_RDWR);
+	if (fd_hdr < 0) {
+		res = fd_hdr;
+		errno = -fd_hdr;
+		qb_util_perror(LOG_ERR, "couldn't open file for mmap");
+		return res;
+	}
+	(void)strlcpy(c->request.u.us.shared_file_name, r->request, NAME_MAX);
+	shm_ptr = mmap(0, SHM_CONTROL_SIZE,
+		       PROT_READ | PROT_WRITE, MAP_SHARED, fd_hdr, 0);
+
+	if (shm_ptr == MAP_FAILED) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "couldn't create mmap for header");
+		goto cleanup_hdr;
+	}
+	c->request.u.us.shared_data = shm_ptr;
+	c->response.u.us.shared_data = shm_ptr + sizeof(struct ipc_us_control);
+	c->event.u.us.shared_data =  shm_ptr + (2 * sizeof(struct ipc_us_control));
+
+	close(fd_hdr);
+	fd_hdr = -1;
+
+	res = qb_ipc_dgram_sock_connect(r->response, "response", "request",
+					r->max_msg_size, &c->request.u.us.sock);
+	if (res != 0) {
+		goto cleanup_hdr;
+	}
+	c->response.u.us.sock = c->request.u.us.sock;
+
+	res = qb_ipc_dgram_sock_connect(r->response, "event", "event-tx",
+					r->max_msg_size, &c->event.u.us.sock);
+	if (res != 0) {
+		goto cleanup_hdr;
+	}
+
+	return 0;
+
+cleanup_hdr:
+	if (fd_hdr >= 0) {
+		close(fd_hdr);
+	}
+	close(c->event.u.us.sock);
+	close(c->request.u.us.sock);
+	unlink(r->request);
+	munmap(c->request.u.us.shared_data, SHM_CONTROL_SIZE);
+	return res;
+}
+
+/*
+ * service functions
+ * --------------------------------------------------------
+ */
+static int32_t
+_sock_connection_liveliness(int32_t fd, int32_t revents, void *data)
+{
+	struct qb_ipcs_connection *c = (struct qb_ipcs_connection *)data;
+
+	qb_util_log(LOG_DEBUG, "LIVENESS: fd %d event %d conn (%s)",
+		    fd, revents, c->description);
+	if (revents & POLLNVAL) {
+		qb_util_log(LOG_DEBUG, "NVAL conn (%s)", c->description);
+		qb_ipcs_disconnect(c);
+		return -EINVAL;
+	}
+	if (revents & POLLHUP) {
+		qb_util_log(LOG_DEBUG, "HUP conn (%s)", c->description);
+		qb_ipcs_disconnect(c);
+		return -ESHUTDOWN;
+	}
+
+	/* If we actually get POLLIN for some reason here, it most
+	 * certainly means EOF. Do a recv on the fd to detect eof and
+	 * then disconnect */
+	if (revents & POLLIN) {
+		char buf[10];
+		int res;
+
+		res = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
+		if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+			res = -errno;
+		} else if (res == 0) {
+			qb_util_log(LOG_DEBUG, "EOF conn (%s)", c->description);
+			res = -ESHUTDOWN;
+		}
+
+		if (res < 0) {
+			qb_ipcs_disconnect(c);
+			return res;
+		}
+	}
+
+	return 0;
+}
+
+static int32_t
+_sock_add_to_mainloop(struct qb_ipcs_connection *c)
+{
+	int res;
+
+	res = c->service->poll_fns.dispatch_add(c->service->poll_priority,
+						c->request.u.us.sock,
+						POLLIN | POLLPRI | POLLNVAL,
+						c,
+						qb_ipcs_dispatch_connection_request);
+
+	if (res < 0) {
+		qb_util_log(LOG_ERR,
+			    "Error adding socket to mainloop (%s).",
+			    c->description);
+		return res;
+	}
+
+	res = c->service->poll_fns.dispatch_add(c->service->poll_priority,
+						c->setup.u.us.sock,
+						POLLIN | POLLPRI | POLLNVAL,
+						c, _sock_connection_liveliness);
+	qb_util_log(LOG_DEBUG, "added %d to poll loop (liveness)",
+		    c->setup.u.us.sock);
+	if (res < 0) {
+		qb_util_perror(LOG_ERR, "Error adding setupfd to mainloop");
+		(void)c->service->poll_fns.dispatch_del(c->request.u.us.sock);
+		return res;
+	}
+	return res;
+}
+
+static void
+_sock_rm_from_mainloop(struct qb_ipcs_connection *c)
+{
+	(void)c->service->poll_fns.dispatch_del(c->request.u.us.sock);
+	(void)c->service->poll_fns.dispatch_del(c->setup.u.us.sock);
+}
+
+static void
+qb_ipcs_us_disconnect(struct qb_ipcs_connection *c)
+{
+	qb_enter();
+
+	if (c->state == QB_IPCS_CONNECTION_ESTABLISHED ||
+	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
+		_sock_rm_from_mainloop(c);
+
+		qb_ipcc_us_sock_close(c->setup.u.us.sock);
+		qb_ipcc_us_sock_close(c->request.u.us.sock);
+		qb_ipcc_us_sock_close(c->event.u.us.sock);
+	}
+	if (c->state == QB_IPCS_CONNECTION_SHUTTING_DOWN ||
+	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
+		munmap(c->request.u.us.shared_data, SHM_CONTROL_SIZE);
+		unlink(c->request.u.us.shared_file_name);
+	}
+}
+
+static int32_t
+qb_ipcs_us_connect(struct qb_ipcs_service *s,
+		   struct qb_ipcs_connection *c,
+		   struct qb_ipc_connection_response *r)
+{
+	char path[PATH_MAX];
+	int32_t fd_hdr;
+	int32_t res = 0;
+	struct ipc_us_control *ctl;
+	char *shm_ptr;
+
+	qb_util_log(LOG_DEBUG, "connecting to client (%s)", c->description);
+
+	c->request.u.us.sock = c->setup.u.us.sock;
+	c->response.u.us.sock = c->setup.u.us.sock;
+
+	snprintf(r->request, NAME_MAX, "qb-%s-control-%s",
+		 s->name, c->description);
+	snprintf(r->response, NAME_MAX, "qb-%s-%s", s->name, c->description);
+
+	fd_hdr = qb_sys_mmap_file_open(path, r->request,
+				       SHM_CONTROL_SIZE,
+				       O_CREAT | O_TRUNC | O_RDWR);
+	if (fd_hdr < 0) {
+		res = fd_hdr;
+		errno = -fd_hdr;
+		qb_util_perror(LOG_ERR, "couldn't create file for mmap (%s)",
+			       c->description);
+		return res;
+	}
+	(void)strlcpy(r->request, path, PATH_MAX);
+	(void)strlcpy(c->request.u.us.shared_file_name, r->request, NAME_MAX);
+	res = chown(r->request, c->auth.uid, c->auth.gid);
+	if (res != 0) {
+		/* ignore res, this is just for the compiler warnings.
+		 */
+		res = 0;
+	}
+	res = chmod(r->request, c->auth.mode);
+	if (res != 0) {
+		/* ignore res, this is just for the compiler warnings.
+		 */
+		res = 0;
+	}
+
+	shm_ptr = mmap(0, SHM_CONTROL_SIZE,
+		       PROT_READ | PROT_WRITE, MAP_SHARED, fd_hdr, 0);
+
+	if (shm_ptr == MAP_FAILED) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "couldn't create mmap for header (%s)",
+			       c->description);
+		goto cleanup_hdr;
+	}
+	c->request.u.us.shared_data = shm_ptr;
+	c->response.u.us.shared_data = shm_ptr + sizeof(struct ipc_us_control);
+	c->event.u.us.shared_data =  shm_ptr + (2 * sizeof(struct ipc_us_control));
+
+	ctl = (struct ipc_us_control *)c->request.u.us.shared_data;
+	ctl->sent = 0;
+	ctl->flow_control = 0;
+	ctl = (struct ipc_us_control *)c->response.u.us.shared_data;
+	ctl->sent = 0;
+	ctl->flow_control = 0;
+	ctl = (struct ipc_us_control *)c->event.u.us.shared_data;
+	ctl->sent = 0;
+	ctl->flow_control = 0;
+
+	close(fd_hdr);
+	fd_hdr = -1;
+
+	/* request channel */
+	res = qb_ipc_dgram_sock_setup(r->response, "request",
+				      &c->request.u.us.sock);
+	if (res < 0) {
+		goto cleanup_hdr;
+	}
+	c->setup.u.us.sock_name = NULL;
+	c->request.u.us.sock_name = NULL;
+
+	/* response channel */
+	c->response.u.us.sock = c->request.u.us.sock;
+	snprintf(path, PATH_MAX, "%s-%s", r->response, "response");
+	c->response.u.us.sock_name = strdup(path);
+
+	/* event channel */
+	res = qb_ipc_dgram_sock_setup(r->response, "event-tx",
+				      &c->event.u.us.sock);
+	if (res < 0) {
+		goto cleanup_hdr;
+	}
+	snprintf(path, PATH_MAX, "%s-%s", r->response, "event");
+	c->event.u.us.sock_name = strdup(path);
+
+	res = _sock_add_to_mainloop(c);
+	if (res < 0) {
+		goto cleanup_hdr;
+	}
+
+	return res;
+
+cleanup_hdr:
+	free(c->response.u.us.sock_name);
+	free(c->event.u.us.sock_name);
+
+	if (fd_hdr >= 0) {
+		close(fd_hdr);
+	}
+	unlink(r->request);
+	munmap(c->request.u.us.shared_data, SHM_CONTROL_SIZE);
+	return res;
+}
+
+void
+qb_ipcs_us_init(struct qb_ipcs_service *s)
+{
+	s->funcs.connect = qb_ipcs_us_connect;
+	s->funcs.disconnect = qb_ipcs_us_disconnect;
+
+	s->funcs.recv = qb_ipc_us_recv_at_most;
+	s->funcs.peek = NULL;
+	s->funcs.reclaim = NULL;
+	s->funcs.send = qb_ipc_socket_send;
+	s->funcs.sendv = qb_ipc_socket_sendv;
+
+	s->funcs.fc_set = qb_ipc_us_fc_set;
+	s->funcs.q_len_get = qb_ipc_us_q_len_get;
+
+	s->needs_sock_for_poll = QB_FALSE;
+
+	qb_atomic_init();
+}
diff --git a/lib/ipcc.c b/lib/ipcc.c
new file mode 100644
index 0000000..f9042c8
--- /dev/null
+++ b/lib/ipcc.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include "ipc_int.h"
+#include "util_int.h"
+#include <qb/qbdefs.h>
+#include <qb/qbipcc.h>
+
+qb_ipcc_connection_t *
+qb_ipcc_connect(const char *name, size_t max_msg_size)
+{
+	int32_t res;
+	qb_ipcc_connection_t *c = NULL;
+	struct qb_ipc_connection_response response;
+
+	c = calloc(1, sizeof(struct qb_ipcc_connection));
+	if (c == NULL) {
+		return NULL;
+	}
+
+	c->setup.max_msg_size = QB_MAX(max_msg_size,
+				       sizeof(struct qb_ipc_connection_response));
+	(void)strlcpy(c->name, name, NAME_MAX);
+	res = qb_ipcc_us_setup_connect(c, &response);
+	if (res < 0) {
+		goto disconnect_and_cleanup;
+	}
+	c->response.type = response.connection_type;
+	c->request.type = response.connection_type;
+	c->event.type = response.connection_type;
+	c->setup.type = response.connection_type;
+
+	c->response.max_msg_size = response.max_msg_size;
+	c->request.max_msg_size = response.max_msg_size;
+	c->event.max_msg_size = response.max_msg_size;
+	c->receive_buf = calloc(1, response.max_msg_size);
+	c->fc_enable_max = 1;
+	if (c->receive_buf == NULL) {
+		res = -ENOMEM;
+		goto disconnect_and_cleanup;
+	}
+
+	switch (c->request.type) {
+	case QB_IPC_SHM:
+		res = qb_ipcc_shm_connect(c, &response);
+		break;
+	case QB_IPC_SOCKET:
+		res = qb_ipcc_us_connect(c, &response);
+		break;
+	case QB_IPC_POSIX_MQ:
+	case QB_IPC_SYSV_MQ:
+		res = -ENOTSUP;
+		break;
+	default:
+		res = -EINVAL;
+		break;
+	}
+	if (res != 0) {
+		goto disconnect_and_cleanup;
+	}
+	c->is_connected = QB_TRUE;
+	return c;
+
+disconnect_and_cleanup:
+	qb_ipcc_us_sock_close(c->setup.u.us.sock);
+	free(c->receive_buf);
+	free(c);
+	errno = -res;
+	return NULL;
+}
+
+static int32_t
+_check_connection_state_with(struct qb_ipcc_connection * c, int32_t res,
+			     struct qb_ipc_one_way * one_way,
+			     int32_t ms_timeout, int32_t events)
+{
+	if (res >= 0) return res;
+
+	if (qb_ipc_us_sock_error_is_disconnected(res)) {
+		errno = -res;
+		qb_util_perror(LOG_DEBUG,
+			       "interpreting result %d as a disconnect",
+			       res);
+		c->is_connected = QB_FALSE;
+	}
+
+	if (res == -EAGAIN || res == -ETIMEDOUT) {
+		int32_t res2;
+		int32_t poll_ms = ms_timeout;
+		if (res == -ETIMEDOUT) {
+			poll_ms = 0;
+		}
+		res2 = qb_ipc_us_ready(one_way, &c->setup, poll_ms, events);
+		if (qb_ipc_us_sock_error_is_disconnected(res2)) {
+			errno = -res2;
+			qb_util_perror(LOG_DEBUG,
+				       "%s %d %s",
+				       "interpreting result",
+				       res2,
+				       "(from socket) as a disconnect");
+			c->is_connected = QB_FALSE;
+			res = res2;
+		} else if (res != -ETIMEDOUT) {
+			/* if the result we're checking against is a TIMEOUT error.
+			 * don't override that result with another error that does
+			 * not imply a disconnect */
+			res = res2;
+		}
+	}
+	return res;
+}
+
+
+static int32_t
+_check_connection_state(struct qb_ipcc_connection * c, int32_t res)
+{
+	if (res >= 0) return res;
+
+	if (qb_ipc_us_sock_error_is_disconnected(res)) {
+		errno = -res;
+		qb_util_perror(LOG_DEBUG,
+			    "interpreting result %d as a disconnect",
+			    res);
+		c->is_connected = QB_FALSE;
+	}
+	return res;
+}
+
+static struct qb_ipc_one_way *
+_event_sock_one_way_get(struct qb_ipcc_connection * c)
+{
+	if (c->needs_sock_for_poll) {
+		return &c->setup;
+	}
+	return &c->event;
+}
+
+static struct qb_ipc_one_way *
+_response_sock_one_way_get(struct qb_ipcc_connection * c)
+{
+	if (c->needs_sock_for_poll) {
+		return &c->setup;
+	}
+	return &c->response;
+}
+
+ssize_t
+qb_ipcc_send(struct qb_ipcc_connection * c, const void *msg_ptr, size_t msg_len)
+{
+	ssize_t res;
+	ssize_t res2;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	if (msg_len > c->request.max_msg_size) {
+		return -EMSGSIZE;
+	}
+	if (c->funcs.fc_get) {
+		res = c->funcs.fc_get(&c->request);
+		if (res < 0) {
+			return res;
+		} else if (res > 0 && res <= c->fc_enable_max) {
+			return -EAGAIN;
+		} else {
+			/*
+			 * we can transmit
+			 */
+		}
+	}
+
+	res = c->funcs.send(&c->request, msg_ptr, msg_len);
+	if (res == msg_len && c->needs_sock_for_poll) {
+		do {
+			res2 = qb_ipc_us_send(&c->setup, msg_ptr, 1);
+		} while (res2 == -EAGAIN);
+		if (res2 == -EPIPE) {
+			res2 = -ENOTCONN;
+		}
+		if (res2 != 1) {
+			res = res2;
+		}
+	}
+	return _check_connection_state(c, res);
+}
+
+int32_t
+qb_ipcc_fc_enable_max_set(struct qb_ipcc_connection * c, uint32_t max)
+{
+	if (c == NULL || max > 2) {
+		return -EINVAL;
+	}
+	c->fc_enable_max = max;
+	return 0;
+}
+
+ssize_t
+qb_ipcc_sendv(struct qb_ipcc_connection * c, const struct iovec * iov,
+	      size_t iov_len)
+{
+	int32_t total_size = 0;
+	int32_t i;
+	int32_t res;
+	int32_t res2;
+
+	for (i = 0; i < iov_len; i++) {
+		total_size += iov[i].iov_len;
+	}
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	if (total_size > c->request.max_msg_size) {
+		return -EMSGSIZE;
+	}
+
+	if (c->funcs.fc_get) {
+		res = c->funcs.fc_get(&c->request);
+		if (res < 0) {
+			return res;
+		} else if (res > 0 && res <= c->fc_enable_max) {
+			return -EAGAIN;
+		} else {
+			/*
+			 * we can transmit
+			 */
+		}
+	}
+
+	res = c->funcs.sendv(&c->request, iov, iov_len);
+	if (res > 0 && c->needs_sock_for_poll) {
+		do {
+			res2 = qb_ipc_us_send(&c->setup, &res, 1);
+		} while (res2 == -EAGAIN);
+		if (res2 == -EPIPE) {
+			res2 = -ENOTCONN;
+		}
+		if (res2 != 1) {
+			res = res2;
+		}
+	}
+	return _check_connection_state(c, res);
+}
+
+ssize_t
+qb_ipcc_recv(struct qb_ipcc_connection * c, void *msg_ptr,
+	     size_t msg_len, int32_t ms_timeout)
+{
+	int32_t res = 0;
+	int32_t connect_res = 0;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+
+	res = c->funcs.recv(&c->response, msg_ptr, msg_len, ms_timeout);
+	if (res >= 0) {
+		return res;
+	}
+
+	/* if we didn't get a msg, check connection state */
+	connect_res = _check_connection_state_with(c, res,
+					    _response_sock_one_way_get(c),
+					    ms_timeout, POLLIN);
+
+	/* only report the connection state check result if an error is returned. */
+	if (connect_res < 0) {
+		return connect_res;
+	}
+	return res;
+}
+
+ssize_t
+qb_ipcc_sendv_recv(qb_ipcc_connection_t * c,
+		   const struct iovec * iov, uint32_t iov_len,
+		   void *res_msg, size_t res_len, int32_t ms_timeout)
+{
+	ssize_t res = 0;
+	int32_t timeout_now;
+	int32_t timeout_rem = ms_timeout;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+
+	if (c->funcs.fc_get) {
+		res = c->funcs.fc_get(&c->request);
+		if (res < 0) {
+			return res;
+		} else if (res > 0 && res <= c->fc_enable_max) {
+			return -EAGAIN;
+		} else {
+			/*
+			 * we can transmit
+			 */
+		}
+	}
+
+	res = qb_ipcc_sendv(c, iov, iov_len);
+	if (res < 0) {
+		return res;
+	}
+
+	do {
+		if (timeout_rem > QB_IPC_MAX_WAIT_MS || ms_timeout == -1) {
+			timeout_now = QB_IPC_MAX_WAIT_MS;
+		} else {
+			timeout_now = timeout_rem;
+		}
+
+		res = qb_ipcc_recv(c, res_msg, res_len, timeout_now);
+		if (res == -ETIMEDOUT) {
+			if (ms_timeout < 0) {
+				res = -EAGAIN;
+			} else {
+				timeout_rem -= timeout_now;
+				if (timeout_rem > 0) {
+					res = -EAGAIN;
+				}
+			}
+		} else	if (res < 0 && res != -EAGAIN) {
+			errno = -res;
+			qb_util_perror(LOG_DEBUG,
+				       "qb_ipcc_recv %d timeout:(%d/%d)",
+				       res, timeout_now, timeout_rem);
+		}
+	} while (res == -EAGAIN && c->is_connected);
+
+	return res;
+}
+
+int32_t
+qb_ipcc_fd_get(struct qb_ipcc_connection * c, int32_t * fd)
+{
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	if (c->event.type == QB_IPC_SOCKET) {
+		*fd = c->event.u.us.sock;
+	} else {
+		*fd = c->setup.u.us.sock;
+	}
+	return 0;
+}
+
+ssize_t
+qb_ipcc_event_recv(struct qb_ipcc_connection * c, void *msg_pt,
+		   size_t msg_len, int32_t ms_timeout)
+{
+	char one_byte = 1;
+	int32_t res;
+	ssize_t size;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	res = _check_connection_state_with(c, -EAGAIN, _event_sock_one_way_get(c),
+					   ms_timeout, POLLIN);
+	if (res < 0) {
+		return res;
+	}
+	size = c->funcs.recv(&c->event, msg_pt, msg_len, ms_timeout);
+	if (size > 0 && c->needs_sock_for_poll) {
+		res = qb_ipc_us_recv(&c->setup, &one_byte, 1, -1);
+		if (res != 1) {
+			size = res;
+		}
+	}
+	return _check_connection_state(c, size);
+}
+
+void
+qb_ipcc_disconnect(struct qb_ipcc_connection *c)
+{
+	struct qb_ipc_one_way *ow = NULL;
+
+	qb_util_log(LOG_DEBUG, "%s()", __func__);
+
+	if (c == NULL) {
+		return;
+	}
+
+	ow = _event_sock_one_way_get(c);
+	(void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
+
+	if (c->funcs.disconnect) {
+		c->funcs.disconnect(c);
+	}
+	free(c->receive_buf);
+	free(c);
+}
+
+void
+qb_ipcc_context_set(struct qb_ipcc_connection *c, void *context)
+{
+	if (c == NULL) {
+		return;
+	}
+	c->context = context;
+}
+
+void *qb_ipcc_context_get(struct qb_ipcc_connection *c)
+{
+	if (c == NULL) {
+		return NULL;
+	}
+	return c->context;
+}
+
+int32_t
+qb_ipcc_is_connected(qb_ipcc_connection_t *c)
+{
+	struct qb_ipc_one_way *ow;
+
+	if (c == NULL) {
+		return QB_FALSE;
+	}
+
+	ow = _response_sock_one_way_get(c);
+	(void)_check_connection_state_with(c, -EAGAIN, ow, 0, POLLIN);
+
+	return c->is_connected;
+}
+
+int32_t
+qb_ipcc_get_buffer_size(qb_ipcc_connection_t * c)
+{
+	if (c == NULL) {
+		return -EINVAL;
+	}
+
+	return c->event.max_msg_size;
+}
diff --git a/lib/ipcs.c b/lib/ipcs.c
new file mode 100644
index 0000000..dd968d6
--- /dev/null
+++ b/lib/ipcs.c
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include "util_int.h"
+#include "ipc_int.h"
+#include <qb/qbdefs.h>
+#include <qb/qbatomic.h>
+#include <qb/qbipcs.h>
+
+static void qb_ipcs_flowcontrol_set(struct qb_ipcs_connection *c,
+				    int32_t fc_enable);
+static int32_t
+new_event_notification(struct qb_ipcs_connection * c);
+
+static QB_LIST_DECLARE(qb_ipc_services);
+
+qb_ipcs_service_t *
+qb_ipcs_create(const char *name,
+	       int32_t service_id,
+	       enum qb_ipc_type type, struct qb_ipcs_service_handlers *handlers)
+{
+	struct qb_ipcs_service *s;
+
+	s = calloc(1, sizeof(struct qb_ipcs_service));
+	if (s == NULL) {
+		return NULL;
+	}
+	if (type == QB_IPC_NATIVE) {
+#ifdef DISABLE_IPC_SHM
+		s->type = QB_IPC_SOCKET;
+#else
+		s->type = QB_IPC_SHM;
+#endif /* DISABLE_IPC_SHM */
+	} else {
+		s->type = type;
+	}
+
+	s->pid = getpid();
+	s->needs_sock_for_poll = QB_FALSE;
+	s->poll_priority = QB_LOOP_MED;
+
+	/* Initial alloc ref */
+	qb_ipcs_ref(s);
+
+	s->service_id = service_id;
+	(void)strlcpy(s->name, name, NAME_MAX);
+
+	s->serv_fns.connection_accept = handlers->connection_accept;
+	s->serv_fns.connection_created = handlers->connection_created;
+	s->serv_fns.msg_process = handlers->msg_process;
+	s->serv_fns.connection_closed = handlers->connection_closed;
+	s->serv_fns.connection_destroyed = handlers->connection_destroyed;
+
+	qb_list_init(&s->connections);
+	qb_list_init(&s->list);
+	qb_list_add(&s->list, &qb_ipc_services);
+
+	return s;
+}
+
+void
+qb_ipcs_poll_handlers_set(struct qb_ipcs_service *s,
+			  struct qb_ipcs_poll_handlers *handlers)
+{
+	s->poll_fns.job_add = handlers->job_add;
+	s->poll_fns.dispatch_add = handlers->dispatch_add;
+	s->poll_fns.dispatch_mod = handlers->dispatch_mod;
+	s->poll_fns.dispatch_del = handlers->dispatch_del;
+}
+
+void
+qb_ipcs_service_context_set(qb_ipcs_service_t* s,
+			    void *context)
+{
+	s->context = context;
+}
+
+void *
+qb_ipcs_service_context_get(qb_ipcs_service_t* s)
+{
+	return s->context;
+}
+
+int32_t
+qb_ipcs_run(struct qb_ipcs_service *s)
+{
+	int32_t res = 0;
+
+	if (s->poll_fns.dispatch_add == NULL ||
+	    s->poll_fns.dispatch_mod == NULL ||
+	    s->poll_fns.dispatch_del == NULL) {
+
+		res = -EINVAL;
+		goto run_cleanup;
+	}
+
+	switch (s->type) {
+	case QB_IPC_SOCKET:
+		qb_ipcs_us_init((struct qb_ipcs_service *)s);
+		break;
+	case QB_IPC_SHM:
+#ifdef DISABLE_IPC_SHM
+		res = -ENOTSUP;
+#else
+		qb_ipcs_shm_init((struct qb_ipcs_service *)s);
+#endif /* DISABLE_IPC_SHM */
+		break;
+	case QB_IPC_POSIX_MQ:
+	case QB_IPC_SYSV_MQ:
+		res = -ENOTSUP;
+		break;
+	default:
+		res = -EINVAL;
+		break;
+	}
+
+	if (res == 0) {
+		res = qb_ipcs_us_publish(s);
+		if (res < 0) {
+			(void)qb_ipcs_us_withdraw(s);
+			goto run_cleanup;
+		}
+	}
+
+run_cleanup:
+	if (res < 0) {
+		/* Failed to run services, removing initial alloc reference. */
+		qb_ipcs_unref(s);
+	}
+
+	return res;
+}
+
+static int32_t
+_modify_dispatch_descriptor_(struct qb_ipcs_connection *c)
+{
+	qb_ipcs_dispatch_mod_fn disp_mod = c->service->poll_fns.dispatch_mod;
+
+	if (c->service->type == QB_IPC_SOCKET) {
+		return disp_mod(c->service->poll_priority,
+				c->event.u.us.sock,
+				c->poll_events, c,
+				qb_ipcs_dispatch_connection_request);
+	} else {
+		return disp_mod(c->service->poll_priority,
+				c->setup.u.us.sock,
+				c->poll_events, c,
+				qb_ipcs_dispatch_connection_request);
+	}
+	return -EINVAL;
+}
+
+void
+qb_ipcs_request_rate_limit(struct qb_ipcs_service *s,
+			   enum qb_ipcs_rate_limit rl)
+{
+	struct qb_ipcs_connection *c;
+	enum qb_loop_priority old_p = s->poll_priority;
+	struct qb_list_head *pos;
+	struct qb_list_head *n;
+
+	switch (rl) {
+	case QB_IPCS_RATE_FAST:
+		s->poll_priority = QB_LOOP_HIGH;
+		break;
+	case QB_IPCS_RATE_SLOW:
+	case QB_IPCS_RATE_OFF:
+	case QB_IPCS_RATE_OFF_2:
+		s->poll_priority = QB_LOOP_LOW;
+		break;
+	default:
+	case QB_IPCS_RATE_NORMAL:
+		s->poll_priority = QB_LOOP_MED;
+		break;
+	}
+
+	qb_list_for_each_safe(pos, n, &s->connections) {
+
+		c = qb_list_entry(pos, struct qb_ipcs_connection, list);
+		qb_ipcs_connection_ref(c);
+
+		if (rl == QB_IPCS_RATE_OFF) {
+			qb_ipcs_flowcontrol_set(c, 1);
+		} else if (rl == QB_IPCS_RATE_OFF_2) {
+			qb_ipcs_flowcontrol_set(c, 2);
+		} else {
+			qb_ipcs_flowcontrol_set(c, QB_FALSE);
+		}
+		if (old_p != s->poll_priority) {
+			(void)_modify_dispatch_descriptor_(c);
+		}
+		qb_ipcs_connection_unref(c);
+	}
+}
+
+void
+qb_ipcs_ref(struct qb_ipcs_service *s)
+{
+	qb_atomic_int_inc(&s->ref_count);
+}
+
+void
+qb_ipcs_unref(struct qb_ipcs_service *s)
+{
+	int32_t free_it;
+
+	assert(s->ref_count > 0);
+	free_it = qb_atomic_int_dec_and_test(&s->ref_count);
+	if (free_it) {
+		qb_util_log(LOG_DEBUG, "%s() - destroying", __func__);
+		free(s);
+	}
+}
+
+void
+qb_ipcs_destroy(struct qb_ipcs_service *s)
+{
+	struct qb_ipcs_connection *c = NULL;
+	struct qb_list_head *pos;
+	struct qb_list_head *n;
+
+	if (s == NULL) {
+		return;
+	}
+	qb_list_for_each_safe(pos, n, &s->connections) {
+		c = qb_list_entry(pos, struct qb_ipcs_connection, list);
+		if (c == NULL) {
+			continue;
+		}
+		qb_ipcs_disconnect(c);
+	}
+	(void)qb_ipcs_us_withdraw(s);
+
+	/* service destroyed, remove initial alloc ref */
+	qb_ipcs_unref(s);
+}
+
+/*
+ * connection API
+ */
+static struct qb_ipc_one_way *
+_event_sock_one_way_get(struct qb_ipcs_connection * c)
+{
+	if (c->service->needs_sock_for_poll) {
+		return &c->setup;
+	}
+	if (c->event.type == QB_IPC_SOCKET) {
+		return &c->event;
+	}
+	return NULL;
+}
+
+static struct qb_ipc_one_way *
+_response_sock_one_way_get(struct qb_ipcs_connection * c)
+{
+	if (c->service->needs_sock_for_poll) {
+		return &c->setup;
+	}
+	if (c->response.type == QB_IPC_SOCKET) {
+		return &c->response;
+	}
+	return NULL;
+}
+
+ssize_t
+qb_ipcs_response_send(struct qb_ipcs_connection *c, const void *data,
+		      size_t size)
+{
+	ssize_t res;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	qb_ipcs_connection_ref(c);
+	res = c->service->funcs.send(&c->response, data, size);
+	if (res == size) {
+		c->stats.responses++;
+	} else if (res == -EAGAIN || res == -ETIMEDOUT) {
+		struct qb_ipc_one_way *ow = _response_sock_one_way_get(c);
+		if (ow) {
+			ssize_t res2 = qb_ipc_us_ready(ow, &c->setup, 0, POLLOUT);
+			if (res2 < 0) {
+				res = res2;
+			}
+		}
+		c->stats.send_retries++;
+	}
+	qb_ipcs_connection_unref(c);
+
+	return res;
+}
+
+ssize_t
+qb_ipcs_response_sendv(struct qb_ipcs_connection * c, const struct iovec * iov,
+		       size_t iov_len)
+{
+	ssize_t res;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	qb_ipcs_connection_ref(c);
+	res = c->service->funcs.sendv(&c->response, iov, iov_len);
+	if (res > 0) {
+		c->stats.responses++;
+	} else if (res == -EAGAIN || res == -ETIMEDOUT) {
+		struct qb_ipc_one_way *ow = _response_sock_one_way_get(c);
+		if (ow) {
+			ssize_t res2 = qb_ipc_us_ready(ow, &c->setup, 0, POLLOUT);
+			if (res2 < 0) {
+				res = res2;
+			}
+		}
+		c->stats.send_retries++;
+	}
+	qb_ipcs_connection_unref(c);
+
+	return res;
+}
+
+static int32_t
+resend_event_notifications(struct qb_ipcs_connection *c)
+{
+	ssize_t res = 0;
+
+	if (!c->service->needs_sock_for_poll) {
+		return res;
+	}
+
+	if (c->outstanding_notifiers > 0) {
+		res = qb_ipc_us_send(&c->setup, c->receive_buf,
+				     c->outstanding_notifiers);
+	}
+	if (res > 0) {
+		c->outstanding_notifiers -= res;
+	}
+
+	assert(c->outstanding_notifiers >= 0);
+	if (c->outstanding_notifiers == 0) {
+		c->poll_events = POLLIN | POLLPRI | POLLNVAL;
+		(void)_modify_dispatch_descriptor_(c);
+	}
+	return res;
+}
+
+static int32_t
+new_event_notification(struct qb_ipcs_connection * c)
+{
+	ssize_t res = 0;
+
+	if (!c->service->needs_sock_for_poll) {
+		return res;
+	}
+
+	assert(c->outstanding_notifiers >= 0);
+	if (c->outstanding_notifiers > 0) {
+		c->outstanding_notifiers++;
+		res = resend_event_notifications(c);
+	} else {
+		res = qb_ipc_us_send(&c->setup, &c->outstanding_notifiers, 1);
+		if (res == -EAGAIN) {
+			/*
+			 * notify the client later, when we can.
+			 */
+			c->outstanding_notifiers++;
+			c->poll_events = POLLOUT | POLLIN | POLLPRI | POLLNVAL;
+			(void)_modify_dispatch_descriptor_(c);
+		}
+	}
+	return res;
+}
+
+ssize_t
+qb_ipcs_event_send(struct qb_ipcs_connection * c, const void *data, size_t size)
+{
+	ssize_t res;
+	ssize_t resn;
+
+	if (c == NULL) {
+		return -EINVAL;
+	} else if (size > c->event.max_msg_size) {
+		return -EMSGSIZE;
+	}
+
+	qb_ipcs_connection_ref(c);
+	res = c->service->funcs.send(&c->event, data, size);
+	if (res == size) {
+		c->stats.events++;
+		resn = new_event_notification(c);
+		if (resn < 0 && resn != -EAGAIN && resn != -ENOBUFS) {
+			errno = -resn;
+			qb_util_perror(LOG_WARNING,
+				       "new_event_notification (%s)",
+				       c->description);
+			res = resn;
+		}
+	} else if (res == -EAGAIN || res == -ETIMEDOUT) {
+		struct qb_ipc_one_way *ow = _event_sock_one_way_get(c);
+
+		if (c->outstanding_notifiers > 0) {
+			resn = resend_event_notifications(c);
+		}
+		if (ow) {
+			resn = qb_ipc_us_ready(ow, &c->setup, 0, POLLOUT);
+			if (resn < 0) {
+				res = resn;
+			}
+		}
+		c->stats.send_retries++;
+	}
+
+	qb_ipcs_connection_unref(c);
+	return res;
+}
+
+ssize_t
+qb_ipcs_event_sendv(struct qb_ipcs_connection * c,
+		    const struct iovec * iov, size_t iov_len)
+{
+	ssize_t res;
+	ssize_t resn;
+
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	qb_ipcs_connection_ref(c);
+
+	res = c->service->funcs.sendv(&c->event, iov, iov_len);
+	if (res > 0) {
+		c->stats.events++;
+		resn = new_event_notification(c);
+		if (resn < 0 && resn != -EAGAIN) {
+			errno = -resn;
+			qb_util_perror(LOG_WARNING,
+				       "new_event_notification (%s)",
+				       c->description);
+			res = resn;
+		}
+	} else if (res == -EAGAIN || res == -ETIMEDOUT) {
+		struct qb_ipc_one_way *ow = _event_sock_one_way_get(c);
+
+		if (c->outstanding_notifiers > 0) {
+			resn = resend_event_notifications(c);
+		}
+		if (ow) {
+			resn = qb_ipc_us_ready(ow, &c->setup, 0, POLLOUT);
+			if (resn < 0) {
+				res = resn;
+			}
+		}
+		c->stats.send_retries++;
+	}
+
+	qb_ipcs_connection_unref(c);
+	return res;
+}
+
+qb_ipcs_connection_t *
+qb_ipcs_connection_first_get(struct qb_ipcs_service * s)
+{
+	struct qb_ipcs_connection *c;
+
+	if (qb_list_empty(&s->connections)) {
+		return NULL;
+	}
+
+	c = qb_list_first_entry(&s->connections, struct qb_ipcs_connection,
+				list);
+	qb_ipcs_connection_ref(c);
+
+	return c;
+}
+
+qb_ipcs_connection_t *
+qb_ipcs_connection_next_get(struct qb_ipcs_service * s,
+			    struct qb_ipcs_connection * current)
+{
+	struct qb_ipcs_connection *c;
+
+	if (current == NULL ||
+	    qb_list_is_last(&current->list, &s->connections)) {
+		return NULL;
+	}
+
+	c = qb_list_first_entry(&current->list, struct qb_ipcs_connection,
+				list);
+	qb_ipcs_connection_ref(c);
+
+	return c;
+}
+
+int32_t
+qb_ipcs_service_id_get(struct qb_ipcs_connection * c)
+{
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	return c->service->service_id;
+}
+
+struct qb_ipcs_connection *
+qb_ipcs_connection_alloc(struct qb_ipcs_service *s)
+{
+	struct qb_ipcs_connection *c =
+	    calloc(1, sizeof(struct qb_ipcs_connection));
+
+	if (c == NULL) {
+		return NULL;
+	}
+
+	c->pid = 0;
+	c->euid = -1;
+	c->egid = -1;
+	c->receive_buf = NULL;
+	c->context = NULL;
+	c->fc_enabled = QB_FALSE;
+	c->state = QB_IPCS_CONNECTION_INACTIVE;
+	c->poll_events = POLLIN | POLLPRI | POLLNVAL;
+
+	c->setup.type = s->type;
+	c->request.type = s->type;
+	c->response.type = s->type;
+	c->event.type = s->type;
+	(void)strlcpy(c->description, "not set yet", CONNECTION_DESCRIPTION);
+
+	/* initial alloc ref */
+	qb_ipcs_connection_ref(c);
+
+	/*
+	 * The connection makes use of the service object. Give the connection
+	 * a reference to the service so we know the service can never be destroyed
+	 * until the connection is done with it.
+	 */
+	qb_ipcs_ref(s);
+	c->service = s;
+	qb_list_init(&c->list);
+
+	return c;
+}
+
+void
+qb_ipcs_connection_ref(struct qb_ipcs_connection *c)
+{
+	if (c) {
+		qb_atomic_int_inc(&c->refcount);
+	}
+}
+
+void
+qb_ipcs_connection_unref(struct qb_ipcs_connection *c)
+{
+	int32_t free_it;
+
+	if (c == NULL) {
+		return;
+	}
+	if (c->refcount < 1) {
+		qb_util_log(LOG_ERR, "ref:%d state:%d (%s)",
+			    c->refcount, c->state, c->description);
+		assert(0);
+	}
+	free_it = qb_atomic_int_dec_and_test(&c->refcount);
+	if (free_it) {
+		qb_list_del(&c->list);
+		if (c->service->serv_fns.connection_destroyed) {
+			c->service->serv_fns.connection_destroyed(c);
+		}
+		c->service->funcs.disconnect(c);
+		/* Let go of the connection's reference to the service */
+		qb_ipcs_unref(c->service);
+		free(c->receive_buf);
+		free(c);
+	}
+}
+
+void
+qb_ipcs_disconnect(struct qb_ipcs_connection *c)
+{
+	int32_t res = 0;
+	qb_loop_job_dispatch_fn rerun_job;
+
+	if (c == NULL) {
+		return;
+	}
+	qb_util_log(LOG_DEBUG, "%s(%s) state:%d",
+		    __func__, c->description, c->state);
+
+	if (c->state == QB_IPCS_CONNECTION_ACTIVE) {
+		c->service->funcs.disconnect(c);
+		c->state = QB_IPCS_CONNECTION_INACTIVE;
+		c->service->stats.closed_connections++;
+
+		/* This removes the initial alloc ref */
+		qb_ipcs_connection_unref(c);
+
+		/* return early as it's an incomplete connection.
+		 */
+		return;
+	}
+	if (c->state == QB_IPCS_CONNECTION_ESTABLISHED) {
+		c->service->funcs.disconnect(c);
+		c->state = QB_IPCS_CONNECTION_SHUTTING_DOWN;
+		c->service->stats.active_connections--;
+		c->service->stats.closed_connections++;
+	}
+	if (c->state == QB_IPCS_CONNECTION_SHUTTING_DOWN) {
+		int scheduled_retry = 0;
+		res = 0;
+		if (c->service->serv_fns.connection_closed) {
+			res = c->service->serv_fns.connection_closed(c);
+		}
+		if (res != 0) {
+			/* OK, so they want the connection_closed
+			 * function re-run */
+			rerun_job =
+			    (qb_loop_job_dispatch_fn) qb_ipcs_disconnect;
+			res = c->service->poll_fns.job_add(QB_LOOP_LOW,
+							   c, rerun_job);
+			if (res == 0) {
+				/* this function is going to be called again.
+				 * so hold off on the unref */
+				scheduled_retry = 1;
+			}
+		}
+
+		if (scheduled_retry == 0) {
+			/* This removes the initial alloc ref */
+			qb_ipcs_connection_unref(c);
+		}
+	}
+}
+
+static void
+qb_ipcs_flowcontrol_set(struct qb_ipcs_connection *c, int32_t fc_enable)
+{
+	if (c == NULL) {
+		return;
+	}
+	if (c->fc_enabled != fc_enable) {
+		c->service->funcs.fc_set(&c->request, fc_enable);
+		c->fc_enabled = fc_enable;
+		c->stats.flow_control_state = fc_enable;
+		c->stats.flow_control_count++;
+	}
+}
+
+static int32_t
+_process_request_(struct qb_ipcs_connection *c, int32_t ms_timeout)
+{
+	int32_t res = 0;
+	ssize_t size;
+	struct qb_ipc_request_header *hdr;
+
+	if (c->service->funcs.peek && c->service->funcs.reclaim) {
+		size = c->service->funcs.peek(&c->request, (void **)&hdr,
+					      ms_timeout);
+	} else {
+		hdr = c->receive_buf;
+		size = c->service->funcs.recv(&c->request,
+					      hdr,
+					      c->request.max_msg_size,
+					      ms_timeout);
+	}
+	if (size < 0) {
+		if (size != -EAGAIN && size != -ETIMEDOUT) {
+			qb_util_perror(LOG_DEBUG,
+				       "recv from client connection failed (%s)",
+				       c->description);
+		} else {
+			c->stats.recv_retries++;
+		}
+		res = size;
+		goto cleanup;
+	} else if (size == 0 || hdr->id == QB_IPC_MSG_DISCONNECT) {
+		qb_util_log(LOG_DEBUG, "client requesting a disconnect (%s)",
+			    c->description);
+		res = -ESHUTDOWN;
+		goto cleanup;
+	} else {
+		c->stats.requests++;
+		res = c->service->serv_fns.msg_process(c, hdr, hdr->size);
+		/* 0 == good, negative == backoff */
+		if (res < 0) {
+			res = -ENOBUFS;
+		} else {
+			res = size;
+		}
+	}
+
+	if (c && c->service->funcs.peek && c->service->funcs.reclaim) {
+		c->service->funcs.reclaim(&c->request);
+	}
+
+cleanup:
+	return res;
+}
+
+#define IPC_REQUEST_TIMEOUT 10
+#define MAX_RECV_MSGS 50
+
+static ssize_t
+_request_q_len_get(struct qb_ipcs_connection *c)
+{
+	ssize_t q_len;
+	if (c->service->funcs.q_len_get) {
+		q_len = c->service->funcs.q_len_get(&c->request);
+		if (q_len <= 0) {
+			return q_len;
+		}
+		if (c->service->poll_priority == QB_LOOP_MED) {
+			q_len = QB_MIN(q_len, 5);
+		} else if (c->service->poll_priority == QB_LOOP_LOW) {
+			q_len = 1;
+		} else {
+			q_len = QB_MIN(q_len, MAX_RECV_MSGS);
+		}
+	} else {
+		q_len = 1;
+	}
+	return q_len;
+}
+
+int32_t
+qb_ipcs_dispatch_connection_request(int32_t fd, int32_t revents, void *data)
+{
+	struct qb_ipcs_connection *c = (struct qb_ipcs_connection *)data;
+	char bytes[MAX_RECV_MSGS];
+	int32_t res = 0;
+	int32_t res2;
+	int32_t recvd = 0;
+	ssize_t avail;
+
+	if (revents & POLLNVAL) {
+		qb_util_log(LOG_DEBUG, "NVAL conn (%s)", c->description);
+		res = -EINVAL;
+		goto dispatch_cleanup;
+	}
+	if (revents & POLLHUP) {
+		qb_util_log(LOG_DEBUG, "HUP conn (%s)", c->description);
+		res = -ESHUTDOWN;
+		goto dispatch_cleanup;
+	}
+
+	if (revents & POLLOUT) {
+		/* try resend events now that fd can write */
+		res = resend_event_notifications(c);
+		if (res < 0 && res != -EAGAIN) {
+			errno = -res;
+			qb_util_perror(LOG_WARNING,
+				       "resend_event_notifications (%s)",
+				       c->description);
+		}
+		/* nothing to read */
+		if ((revents & POLLIN) == 0) {
+			res = 0;
+			goto dispatch_cleanup;
+		}
+	}
+	if (c->fc_enabled) {
+		res = 0;
+		goto dispatch_cleanup;
+	}
+	avail = _request_q_len_get(c);
+
+	if (c->service->needs_sock_for_poll && avail == 0) {
+		res2 = qb_ipc_us_recv(&c->setup, bytes, 1, 0);
+		if (qb_ipc_us_sock_error_is_disconnected(res2)) {
+			errno = -res2;
+			qb_util_perror(LOG_WARNING, "conn (%s) disconnected",
+				       c->description);
+			res = -ESHUTDOWN;
+			goto dispatch_cleanup;
+		} else {
+			qb_util_log(LOG_WARNING,
+				    "conn (%s) Nothing in q but got POLLIN on fd:%d (res2:%d)",
+				    c->description, fd, res2);
+			res = 0;
+			goto dispatch_cleanup;
+		}
+	}
+
+	do {
+		res = _process_request_(c, IPC_REQUEST_TIMEOUT);
+
+		if (res == -ESHUTDOWN) {
+			goto dispatch_cleanup;
+		}
+
+		if (res > 0 || res == -ENOBUFS || res == -EINVAL) {
+			recvd++;
+		}
+		if (res > 0) {
+			avail--;
+		}
+	} while (avail > 0 && res > 0 && !c->fc_enabled);
+
+	if (c->service->needs_sock_for_poll && recvd > 0) {
+		res2 = qb_ipc_us_recv(&c->setup, bytes, recvd, -1);
+		if (qb_ipc_us_sock_error_is_disconnected(res2)) {
+			errno = -res2;
+			qb_util_perror(LOG_ERR, "error receiving from setup sock (%s)", c->description);
+
+			res = -ESHUTDOWN;
+			goto dispatch_cleanup;
+		}
+	}
+
+	res = QB_MIN(0, res);
+	if (res == -EAGAIN || res == -ETIMEDOUT || res == -ENOBUFS) {
+		res = 0;
+	}
+	if (res != 0) {
+		if (res != -ENOTCONN) {
+			/*
+			 * Abnormal state (ENOTCONN is normal shutdown).
+			 */
+			errno = -res;
+			qb_util_perror(LOG_ERR, "request returned error (%s)",
+				       c->description);
+		}
+	}
+
+dispatch_cleanup:
+	if (res != 0) {
+		qb_ipcs_disconnect(c);
+	}
+	return res;
+}
+
+void
+qb_ipcs_context_set(struct qb_ipcs_connection *c, void *context)
+{
+	if (c == NULL) {
+		return;
+	}
+	c->context = context;
+}
+
+void *
+qb_ipcs_context_get(struct qb_ipcs_connection *c)
+{
+	if (c == NULL) {
+		return NULL;
+	}
+	return c->context;
+}
+
+void *
+qb_ipcs_connection_service_context_get(qb_ipcs_connection_t *c)
+{
+	if (c == NULL || c->service == NULL) {
+		return NULL;
+	}
+	return c->service->context;
+}
+
+int32_t
+qb_ipcs_connection_stats_get(qb_ipcs_connection_t * c,
+			     struct qb_ipcs_connection_stats * stats,
+			     int32_t clear_after_read)
+{
+	if (c == NULL) {
+		return -EINVAL;
+	}
+	memcpy(stats, &c->stats, sizeof(struct qb_ipcs_connection_stats));
+	if (clear_after_read) {
+		memset(&c->stats, 0, sizeof(struct qb_ipcs_connection_stats_2));
+		c->stats.client_pid = c->pid;
+	}
+	return 0;
+}
+
+struct qb_ipcs_connection_stats_2*
+qb_ipcs_connection_stats_get_2(qb_ipcs_connection_t *c,
+			       int32_t clear_after_read)
+{
+	struct qb_ipcs_connection_stats_2 * stats;
+
+	if (c == NULL) {
+		errno = EINVAL;
+		return NULL;
+	}
+	stats = calloc(1, sizeof(struct qb_ipcs_connection_stats_2));
+	if (stats == NULL) {
+		return NULL;
+	}
+
+	memcpy(stats, &c->stats, sizeof(struct qb_ipcs_connection_stats_2));
+
+	if (c->service->funcs.q_len_get) {
+		stats->event_q_length = c->service->funcs.q_len_get(&c->event);
+	} else {
+		stats->event_q_length = 0;
+	}
+	if (clear_after_read) {
+		memset(&c->stats, 0, sizeof(struct qb_ipcs_connection_stats_2));
+		c->stats.client_pid = c->pid;
+	}
+	return stats;
+}
+
+int32_t
+qb_ipcs_stats_get(struct qb_ipcs_service * s,
+		  struct qb_ipcs_stats * stats, int32_t clear_after_read)
+{
+	if (s == NULL) {
+		return -EINVAL;
+	}
+	memcpy(stats, &s->stats, sizeof(struct qb_ipcs_stats));
+	if (clear_after_read) {
+		memset(&s->stats, 0, sizeof(struct qb_ipcs_stats));
+	}
+	return 0;
+}
+
+void
+qb_ipcs_connection_auth_set(qb_ipcs_connection_t *c, uid_t uid,
+			    gid_t gid, mode_t mode)
+{
+	if (c) {
+		c->auth.uid = uid;
+		c->auth.gid = gid;
+		c->auth.mode = mode;
+	}
+}
+
+int32_t
+qb_ipcs_connection_get_buffer_size(qb_ipcs_connection_t *c)
+{
+	if (c == NULL) {
+		return -EINVAL;
+	}
+
+	/* request, response, and event shoud all have the same
+	 * buffer size allocated. It doesn't matter which we return
+	 * here. */
+	return c->response.max_msg_size;
+}
+
+void qb_ipcs_enforce_buffer_size(qb_ipcs_service_t *s, uint32_t buf_size)
+{
+	if (s == NULL) {
+		return;
+	}
+	s->max_buffer_size = buf_size;
+}
diff --git a/lib/libqb.pc.in b/lib/libqb.pc.in
new file mode 100644
index 0000000..8a8d0ba
--- /dev/null
+++ b/lib/libqb.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: libqb
+Version: @PACKAGE_VERSION@
+Description: libqb
+Requires:
+Libs: -L${libdir} -lqb @LIBS@
+Cflags: -I${includedir}
diff --git a/lib/log.c b/lib/log.c
new file mode 100644
index 0000000..142c2f7
--- /dev/null
+++ b/lib/log.c
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <ctype.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif /* HAVE_LINK_H */
+#include <stdarg.h>
+#include <pthread.h>
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif /* HAVE_DLFCN_H */
+#include <stdarg.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qblog.h>
+#include <qb/qbutil.h>
+#include <qb/qbarray.h>
+#include "log_int.h"
+#include "util_int.h"
+#include <regex.h>
+
+static struct qb_log_target conf[QB_LOG_TARGET_MAX];
+static uint32_t conf_active_max = 0;
+static int32_t in_logger = QB_FALSE;
+static int32_t logger_inited = QB_FALSE;
+static pthread_rwlock_t _listlock;
+static qb_log_filter_fn _custom_filter_fn = NULL;
+
+static QB_LIST_DECLARE(tags_head);
+static QB_LIST_DECLARE(callsite_sections);
+
+struct callsite_section {
+	struct qb_log_callsite *start;
+	struct qb_log_callsite *stop;
+	struct qb_list_head list;
+};
+
+static int32_t _log_target_enable(struct qb_log_target *t);
+static void _log_target_disable(struct qb_log_target *t);
+static void _log_filter_apply(struct callsite_section *sect,
+			      uint32_t t, enum qb_log_filter_conf c,
+			      enum qb_log_filter_type type,
+			      const char *text,
+			      regex_t *regex,
+			      uint8_t high_priority, uint8_t low_priority);
+static void _log_filter_apply_to_cs(struct qb_log_callsite *cs,
+				    uint32_t t, enum qb_log_filter_conf c,
+				    enum qb_log_filter_type type,
+				    const char *text,
+				    regex_t *regex,
+				    uint8_t high_priority, uint8_t low_priority);
+
+/* deprecated method of getting internal log messages */
+static qb_util_log_fn_t old_internal_log_fn = NULL;
+void
+qb_util_set_log_function(qb_util_log_fn_t fn)
+{
+	old_internal_log_fn = fn;
+}
+
+static void
+_log_free_filter(struct qb_log_filter *flt)
+{
+	if (flt->regex) {
+		regfree(flt->regex);
+	}
+	free(flt->regex);
+	free(flt->text);
+	free(flt);
+}
+
+static int32_t
+_cs_matches_filter_(struct qb_log_callsite *cs,
+		    enum qb_log_filter_type type,
+		    const char *text,
+		    regex_t *regex,
+		    uint8_t high_priority,
+		    uint8_t low_priority)
+{
+	int32_t match = QB_FALSE;
+	const char *offset = NULL;
+	const char *next = NULL;
+
+	if (cs->priority > low_priority ||
+	    cs->priority < high_priority) {
+		return QB_FALSE;
+	}
+	if (strcmp(text, "*") == 0) {
+		return QB_TRUE;
+	}
+
+	switch (type) {
+	case QB_LOG_FILTER_FILE:
+	case QB_LOG_FILTER_FUNCTION:
+		next = text;
+		do {
+			char token[500];
+			offset = next;
+			next = strchrnul(offset, ',');
+			snprintf(token, 499, "%.*s", (int)(next - offset), offset);
+
+			if (type == QB_LOG_FILTER_FILE) {
+				match = (strcmp(cs->filename, token) == 0) ? 1 : 0;
+			} else {
+				match = (strcmp(cs->function, token) == 0) ? 1 : 0;
+			}
+			if (!match && next[0] != 0) {
+				next++;
+			}
+
+		} while (match == QB_FALSE && next != NULL && next[0] != 0);
+		break;
+	case QB_LOG_FILTER_FILE_REGEX:
+		next = next ? next : cs->filename;
+	case QB_LOG_FILTER_FUNCTION_REGEX:
+		next = next ? next : cs->function;
+	case QB_LOG_FILTER_FORMAT_REGEX:
+		next = next ? next : cs->format;
+
+		if (regex == NULL) {
+			return QB_FALSE;
+		}
+		match = regexec(regex, next, 0, NULL, 0);
+		if (match == 0) {
+			match = QB_TRUE;
+		} else {
+			match = QB_FALSE;
+		}
+		break;
+	case QB_LOG_FILTER_FORMAT:
+		if (strstr(cs->format, text)) {
+			match = QB_TRUE;
+		}
+		break;
+	}
+
+	return match;
+}
+
+void
+qb_log_real_va_(struct qb_log_callsite *cs, va_list ap)
+{
+	int32_t found_threaded;
+	struct qb_log_target *t;
+	struct timespec tv;
+	int32_t pos;
+	int len;
+	int32_t formatted = QB_FALSE;
+	char buf[QB_LOG_MAX_LEN];
+	char *str = buf;
+	va_list ap_copy;
+
+	if (in_logger || cs == NULL) {
+		return;
+	}
+	in_logger = QB_TRUE;
+
+	if (old_internal_log_fn) {
+		if (qb_bit_is_set(cs->tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
+			if (!formatted) {
+				va_copy(ap_copy, ap);
+				len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
+				va_end(ap_copy);
+				if (len > QB_LOG_MAX_LEN)
+					len = QB_LOG_MAX_LEN;
+				if (str[len - 1] == '\n') str[len - 1] = '\0';
+				formatted = QB_TRUE;
+			}
+			old_internal_log_fn(cs->filename, cs->lineno,
+					    cs->priority, str);
+		}
+	}
+
+	qb_util_timespec_from_epoch_get(&tv);
+
+	/*
+	 * 1 if we can find a threaded target that needs this log then post it
+	 * 2 foreach non-threaded target call it's logger function
+	 */
+	found_threaded = QB_FALSE;
+
+	for (pos = 0; pos <= conf_active_max; pos++) {
+		t = &conf[pos];
+		if (t->state != QB_LOG_STATE_ENABLED) {
+			continue;
+		}
+		if (t->threaded) {
+			if (!found_threaded
+			    && qb_bit_is_set(cs->targets, t->pos)) {
+				found_threaded = QB_TRUE;
+				if (!formatted) {
+					va_copy(ap_copy, ap);
+					len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
+					va_end(ap_copy);
+					if (len > QB_LOG_MAX_LEN)
+						len = QB_LOG_MAX_LEN;
+					if (str[len - 1] == '\n') str[len - 1] = '\0';
+					formatted = QB_TRUE;
+				}
+			}
+		} else {
+			if (qb_bit_is_set(cs->targets, t->pos)) {
+				if (t->vlogger) {
+					va_copy(ap_copy, ap);
+					t->vlogger(t->pos, cs, tv.tv_sec, ap_copy);
+					va_end(ap_copy);
+				} else if (t->logger) {
+					if (!formatted) {
+						va_copy(ap_copy, ap);
+						len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
+						va_end(ap_copy);
+						if (len > QB_LOG_MAX_LEN)
+							len = QB_LOG_MAX_LEN;
+						if (str[len - 1] == '\n') str[len - 1] = '\0';
+						formatted = QB_TRUE;
+					}
+					t->logger(t->pos, cs, tv.tv_sec, str);
+				}
+			}
+		}
+	}
+
+	if (found_threaded) {
+		qb_log_thread_log_post(cs, tv.tv_sec, str);
+	}
+	in_logger = QB_FALSE;
+}
+
+void
+qb_log_real_(struct qb_log_callsite *cs, ...)
+{
+	va_list ap;
+
+	va_start(ap, cs);
+	qb_log_real_va_(cs, ap);
+	va_end(ap);
+}
+
+void
+qb_log_thread_log_write(struct qb_log_callsite *cs,
+			time_t timestamp, const char *buffer)
+{
+	struct qb_log_target *t;
+	int32_t pos;
+
+	for (pos = 0; pos <= conf_active_max; pos++) {
+		t = &conf[pos];
+		if (t->state != QB_LOG_STATE_ENABLED) {
+			continue;
+		}
+		if (!t->threaded) {
+			continue;
+		}
+		if (qb_bit_is_set(cs->targets, t->pos)) {
+			t->logger(t->pos, cs, timestamp, buffer);
+		}
+	}
+}
+
+struct qb_log_callsite*
+qb_log_callsite_get(const char *function,
+		    const char *filename,
+		    const char *format,
+		    uint8_t priority,
+		    uint32_t lineno,
+		    uint32_t tags)
+{
+	struct qb_log_target *t;
+	struct qb_log_filter *flt;
+	struct qb_log_callsite *cs;
+	int32_t new_dcs = QB_FALSE;
+	struct qb_list_head *f_item;
+	int32_t pos;
+
+	if (!logger_inited) {
+		return NULL;
+	}
+
+	cs = qb_log_dcs_get(&new_dcs, function, filename,
+			    format, priority, lineno, tags);
+	if (cs == NULL) {
+		return NULL;
+	}
+
+	if (new_dcs) {
+		pthread_rwlock_rdlock(&_listlock);
+		for (pos = 0; pos <= conf_active_max; pos++) {
+			t = &conf[pos];
+			if (t->state != QB_LOG_STATE_ENABLED) {
+				continue;
+			}
+			qb_list_for_each(f_item, &t->filter_head) {
+				flt = qb_list_entry(f_item, struct qb_log_filter, list);
+				_log_filter_apply_to_cs(cs, t->pos, flt->conf, flt->type,
+							flt->text, flt->regex, flt->high_priority,
+							flt->low_priority);
+			}
+		}
+		if (tags == 0) {
+			qb_list_for_each(f_item, &tags_head) {
+				flt = qb_list_entry(f_item, struct qb_log_filter, list);
+				_log_filter_apply_to_cs(cs, flt->new_value, flt->conf, flt->type,
+							flt->text, flt->regex, flt->high_priority,
+							flt->low_priority);
+			}
+		} else {
+			cs->tags = tags;
+		}
+		if (_custom_filter_fn) {
+			_custom_filter_fn(cs);
+		}
+		pthread_rwlock_unlock(&_listlock);
+	} else if (cs->tags != tags) {
+		cs->tags = tags;
+		if (_custom_filter_fn) {
+			_custom_filter_fn(cs);
+		}
+	}
+	return cs;
+}
+
+void
+qb_log_from_external_source_va(const char *function,
+			       const char *filename,
+			       const char *format,
+			       uint8_t priority,
+			       uint32_t lineno, uint32_t tags, va_list ap)
+{
+	struct qb_log_callsite *cs;
+
+	if (!logger_inited) {
+		return;
+	}
+
+	cs = qb_log_callsite_get(function, filename,
+				 format, priority, lineno, tags);
+	qb_log_real_va_(cs, ap);
+}
+
+void
+qb_log_from_external_source(const char *function,
+			    const char *filename,
+			    const char *format,
+			    uint8_t priority,
+			    uint32_t lineno, uint32_t tags, ...)
+{
+	struct qb_log_callsite *cs;
+	va_list ap;
+
+	if (!logger_inited) {
+		return;
+	}
+
+	cs = qb_log_callsite_get(function, filename,
+				 format, priority, lineno, tags);
+	va_start(ap, tags);
+	qb_log_real_va_(cs, ap);
+	va_end(ap);
+}
+
+int32_t
+qb_log_callsites_register(struct qb_log_callsite *_start,
+			  struct qb_log_callsite *_stop)
+{
+	struct callsite_section *sect;
+	struct qb_log_callsite *cs;
+	struct qb_log_target *t;
+	struct qb_log_filter *flt;
+	int32_t pos;
+
+	if (_start == NULL || _stop == NULL) {
+		return -EINVAL;
+	}
+
+	pthread_rwlock_rdlock(&_listlock);
+	qb_list_for_each_entry(sect, &callsite_sections, list) {
+		if (sect->start == _start || sect->stop == _stop) {
+			pthread_rwlock_unlock(&_listlock);
+			return -EEXIST;
+		}
+	}
+	pthread_rwlock_unlock(&_listlock);
+
+	sect = calloc(1, sizeof(struct callsite_section));
+	if (sect == NULL) {
+		return -ENOMEM;
+	}
+	sect->start = _start;
+	sect->stop = _stop;
+	qb_list_init(&sect->list);
+
+	pthread_rwlock_wrlock(&_listlock);
+	qb_list_add(&sect->list, &callsite_sections);
+
+	/*
+	 * Now apply the filters on these new callsites
+	 */
+	for (pos = 0; pos <= conf_active_max; pos++) {
+		t = &conf[pos];
+		if (t->state != QB_LOG_STATE_ENABLED) {
+			continue;
+		}
+		qb_list_for_each_entry(flt, &t->filter_head, list) {
+			_log_filter_apply(sect, t->pos, flt->conf,
+					  flt->type, flt->text, flt->regex,
+					  flt->high_priority, flt->low_priority);
+		}
+	}
+	qb_list_for_each_entry(flt, &tags_head, list) {
+		_log_filter_apply(sect, flt->new_value, flt->conf,
+				  flt->type, flt->text, flt->regex,
+				  flt->high_priority, flt->low_priority);
+	}
+	pthread_rwlock_unlock(&_listlock);
+	if (_custom_filter_fn) {
+		for (cs = sect->start; cs < sect->stop; cs++) {
+			if (cs->lineno > 0) {
+				_custom_filter_fn(cs);
+			}
+		}
+	}
+	/* qb_log_callsites_dump_sect(sect); */
+
+	return 0;
+}
+
+static void
+qb_log_callsites_dump_sect(struct callsite_section *sect)
+{
+	struct qb_log_callsite *cs;
+
+	printf(" start %p - stop %p\n", sect->start, sect->stop);
+	printf("filename    lineno targets         tags\n");
+	for (cs = sect->start; cs < sect->stop; cs++) {
+		if (cs->lineno > 0) {
+			printf("%12s %6d %16d %16d\n", cs->filename, cs->lineno,
+			       cs->targets, cs->tags);
+		}
+	}
+}
+
+void
+qb_log_callsites_dump(void)
+{
+	struct callsite_section *sect;
+	int32_t l;
+
+	pthread_rwlock_rdlock(&_listlock);
+	l = qb_list_length(&callsite_sections);
+	printf("Callsite Database [%d]\n", l);
+	printf("---------------------\n");
+	qb_list_for_each_entry(sect, &callsite_sections, list) {
+		qb_log_callsites_dump_sect(sect);
+	}
+	pthread_rwlock_unlock(&_listlock);
+}
+
+static int32_t
+_log_filter_exists(struct qb_list_head *list_head,
+		   enum qb_log_filter_type type,
+		   const char *text,
+		   uint8_t high_priority,
+		   uint8_t low_priority,
+		   uint32_t new_value)
+{
+	struct qb_log_filter *flt;
+
+	qb_list_for_each_entry(flt, list_head, list) {
+		if (flt->type == type &&
+		    flt->high_priority == high_priority &&
+		    flt->low_priority == low_priority &&
+		    flt->new_value == new_value &&
+		    strcmp(flt->text, text) == 0) {
+			return QB_TRUE;
+		}
+	}
+	return QB_FALSE;
+}
+
+static int32_t
+_log_filter_store(uint32_t t, enum qb_log_filter_conf c,
+		  enum qb_log_filter_type type,
+		  const char *text,
+		  uint8_t high_priority,
+		  uint8_t low_priority,
+		  struct qb_log_filter **new)
+{
+	struct qb_log_filter *flt;
+	struct qb_list_head *iter;
+	struct qb_list_head *next;
+	struct qb_list_head *list_head;
+
+	switch (c) {
+	case QB_LOG_FILTER_ADD:
+	case QB_LOG_FILTER_REMOVE:
+	case QB_LOG_FILTER_CLEAR_ALL:
+		list_head = &conf[t].filter_head;
+		break;
+
+	case QB_LOG_TAG_SET:
+	case QB_LOG_TAG_CLEAR:
+	case QB_LOG_TAG_CLEAR_ALL:
+		list_head = &tags_head;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	if (c == QB_LOG_FILTER_ADD || c == QB_LOG_TAG_SET) {
+		if (text == NULL) {
+			return -EINVAL;
+		}
+		if (_log_filter_exists(list_head, type, text,
+				       high_priority, low_priority, t)) {
+			return -EEXIST;
+		}
+		flt = calloc(1, sizeof(struct qb_log_filter));
+		if (flt == NULL) {
+			return -ENOMEM;
+		}
+		qb_list_init(&flt->list);
+		flt->conf = c;
+		flt->type = type;
+		flt->text = strdup(text);
+		if (flt->text == NULL) {
+			_log_free_filter(flt);
+			return -ENOMEM;
+		}
+
+		if (type == QB_LOG_FILTER_FUNCTION_REGEX ||
+			type == QB_LOG_FILTER_FILE_REGEX ||
+			type == QB_LOG_FILTER_FORMAT_REGEX) {
+			int res;
+
+			flt->regex = calloc(1, sizeof(regex_t));
+			if (flt->regex == NULL) {
+				_log_free_filter(flt);
+				return -ENOMEM;
+			}
+			res = regcomp(flt->regex, flt->text, 0);
+			if (res) {
+				_log_free_filter(flt);
+				return -EINVAL;
+			}
+		}
+		flt->high_priority = high_priority;
+		flt->low_priority = low_priority;
+		flt->new_value = t;
+		qb_list_add_tail(&flt->list, list_head);
+		if (new) {
+			*new = flt;
+		}
+	} else if (c == QB_LOG_FILTER_REMOVE || c == QB_LOG_TAG_CLEAR) {
+		qb_list_for_each_safe(iter, next, list_head) {
+			flt = qb_list_entry(iter, struct qb_log_filter, list);
+			if (flt->type == type &&
+			    flt->low_priority <= low_priority &&
+			    flt->high_priority >= high_priority &&
+			    (strcmp(flt->text, text) == 0 ||
+			     strcmp("*", text) == 0)) {
+				qb_list_del(iter);
+				_log_free_filter(flt);
+				return 0;
+			}
+		}
+
+	} else if (c == QB_LOG_FILTER_CLEAR_ALL || c == QB_LOG_TAG_CLEAR_ALL) {
+		qb_list_for_each_safe(iter, next, list_head) {
+			flt = qb_list_entry(iter, struct qb_log_filter, list);
+			qb_list_del(iter);
+			_log_free_filter(flt);
+		}
+	}
+	return 0;
+}
+
+static void
+_log_filter_apply(struct callsite_section *sect,
+		  uint32_t t, enum qb_log_filter_conf c,
+		  enum qb_log_filter_type type,
+		  const char *text,
+		  regex_t *regex,
+		  uint8_t high_priority, uint8_t low_priority)
+{
+	struct qb_log_callsite *cs;
+
+	for (cs = sect->start; cs < sect->stop; cs++) {
+		if (cs->lineno > 0) {
+			_log_filter_apply_to_cs(cs, t, c, type, text, regex,
+					    high_priority, low_priority);
+		}
+	}
+}
+
+/* #define _QB_FILTER_DEBUGGING_ 1 */
+static void
+_log_filter_apply_to_cs(struct qb_log_callsite *cs,
+			uint32_t t, enum qb_log_filter_conf c,
+			enum qb_log_filter_type type,
+			const char *text,
+			regex_t *regex,
+			uint8_t high_priority, uint8_t low_priority)
+{
+
+	if (c == QB_LOG_FILTER_CLEAR_ALL) {
+		qb_bit_clear(cs->targets, t);
+		return;
+	} else if (c == QB_LOG_TAG_CLEAR_ALL) {
+		cs->tags = 0;
+		return;
+	}
+
+	if (_cs_matches_filter_(cs, type, text, regex, high_priority, low_priority)) {
+#ifdef _QB_FILTER_DEBUGGING_
+		uint32_t old_targets = cs->targets;
+		uint32_t old_tags = cs->tags;
+#endif /* _QB_FILTER_DEBUGGING_ */
+		if (c == QB_LOG_FILTER_ADD) {
+			qb_bit_set(cs->targets, t);
+		} else if (c == QB_LOG_FILTER_REMOVE) {
+			qb_bit_clear(cs->targets, t);
+		} else if (c == QB_LOG_TAG_SET) {
+			cs->tags = t;
+		} else if (c == QB_LOG_TAG_CLEAR) {
+			cs->tags = 0;
+		}
+#ifdef _QB_FILTER_DEBUGGING_
+		if (old_targets != cs->targets) {
+			printf("targets: %s:%u value(%d) %d -> %d\n",
+			       cs->filename, cs->lineno, t,
+			       old_targets, cs->targets);
+		}
+		if (old_tags != cs->tags) {
+			printf("tags: %s:%u value(%d) %d -> %d\n",
+			       cs->filename, cs->lineno, t, old_tags, cs->tags);
+		}
+#endif /* _QB_FILTER_DEBUGGING_ */
+	}
+}
+
+int32_t
+qb_log_filter_ctl2(int32_t t, enum qb_log_filter_conf c,
+		   enum qb_log_filter_type type, const char * text,
+		   uint8_t high_priority, uint8_t low_priority)
+{
+	struct qb_log_filter *new_flt = NULL;
+	regex_t *regex = NULL;
+	struct callsite_section *sect;
+	int32_t rc;
+
+	if (!logger_inited) {
+		return -EINVAL;
+	}
+
+	if (c == QB_LOG_FILTER_ADD ||
+	    c == QB_LOG_FILTER_CLEAR_ALL ||
+	    c == QB_LOG_FILTER_REMOVE) {
+		if (t < 0 || t >= QB_LOG_TARGET_MAX ||
+		    conf[t].state == QB_LOG_STATE_UNUSED) {
+			return -EBADF;
+		}
+	}
+
+	if (text == NULL ||
+	    low_priority < high_priority ||
+	    type > QB_LOG_FILTER_FORMAT_REGEX ||
+	    c > QB_LOG_TAG_CLEAR_ALL) {
+		return -EINVAL;
+	}
+	pthread_rwlock_rdlock(&_listlock);
+	rc = _log_filter_store(t, c, type, text, high_priority, low_priority, &new_flt);
+	if (rc < 0) {
+		pthread_rwlock_unlock(&_listlock);
+		return rc;
+	}
+
+	if (new_flt && new_flt->regex) {
+		regex = new_flt->regex;
+	}
+	qb_list_for_each_entry(sect, &callsite_sections, list) {
+		_log_filter_apply(sect, t, c, type, text, regex, high_priority, low_priority);
+	}
+	pthread_rwlock_unlock(&_listlock);
+	return 0;
+}
+
+int32_t
+qb_log_filter_fn_set(qb_log_filter_fn fn)
+{
+	struct callsite_section *sect;
+	struct qb_log_callsite *cs;
+
+	if (!logger_inited) {
+		return -EINVAL;
+	}
+	_custom_filter_fn = fn;
+	if (_custom_filter_fn == NULL) {
+		return 0;
+	}
+
+	qb_list_for_each_entry(sect, &callsite_sections, list) {
+		for (cs = sect->start; cs < sect->stop; cs++) {
+			if (cs->lineno > 0) {
+				_custom_filter_fn(cs);
+			}
+		}
+	}
+	return 0;
+}
+
+int32_t
+qb_log_filter_ctl(int32_t t, enum qb_log_filter_conf c,
+		  enum qb_log_filter_type type,
+		  const char *text, uint8_t priority)
+{
+	return qb_log_filter_ctl2(t, c, type, text, LOG_EMERG, priority);
+}
+
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
+static int32_t
+_log_so_walk_callback(struct dl_phdr_info *info, size_t size, void *data)
+{
+	if (strlen(info->dlpi_name) > 0) {
+		void *handle;
+		void *start;
+		void *stop;
+		const char *error;
+
+		handle = dlopen(info->dlpi_name, RTLD_LAZY);
+		error = dlerror();
+		if (!handle || error) {
+			qb_log(LOG_ERR, "%s", error);
+			if (handle) {
+				dlclose(handle);
+			}
+			return 0;
+		}
+
+		start = dlsym(handle, "__start___verbose");
+		error = dlerror();
+		if (error) {
+			goto done;
+		}
+
+		stop = dlsym(handle, "__stop___verbose");
+		error = dlerror();
+		if (error) {
+			goto done;
+
+		} else {
+			qb_log_callsites_register(start, stop);
+		}
+done:
+		dlclose(handle);
+	}
+	return 0;
+}
+#endif /* QB_HAVE_ATTRIBUTE_SECTION */
+
+static void
+_log_target_state_set(struct qb_log_target *t, enum qb_log_target_state s)
+{
+	int32_t i;
+	int32_t a_set = QB_FALSE;
+	int32_t u_set = QB_FALSE;
+
+	t->state = s;
+
+	for (i = 31; i >= 0; i--) {
+		if (!a_set && conf[i].state == QB_LOG_STATE_ENABLED) {
+			a_set = QB_TRUE;
+			conf_active_max = i;
+		}
+		if (!u_set && conf[i].state != QB_LOG_STATE_UNUSED) {
+			u_set = QB_TRUE;
+		}
+	}
+}
+
+void
+qb_log_init(const char *name, int32_t facility, uint8_t priority)
+{
+	int32_t i;
+
+	i = pthread_rwlock_init(&_listlock, NULL);
+	assert(i == 0);
+	qb_log_format_init();
+
+	for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
+		conf[i].pos = i;
+		conf[i].debug = QB_FALSE;
+		conf[i].file_sync = QB_FALSE;
+		conf[i].state = QB_LOG_STATE_UNUSED;
+		(void)strlcpy(conf[i].name, name, PATH_MAX);
+		conf[i].facility = facility;
+		qb_list_init(&conf[i].filter_head);
+	}
+
+	qb_log_dcs_init();
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
+	qb_log_callsites_register(__start___verbose, __stop___verbose);
+	dl_iterate_phdr(_log_so_walk_callback, NULL);
+#endif /* QB_HAVE_ATTRIBUTE_SECTION */
+
+	conf[QB_LOG_STDERR].state = QB_LOG_STATE_DISABLED;
+	conf[QB_LOG_BLACKBOX].state = QB_LOG_STATE_DISABLED;
+	conf[QB_LOG_STDOUT].state = QB_LOG_STATE_DISABLED;
+
+	logger_inited = QB_TRUE;
+	(void)qb_log_syslog_open(&conf[QB_LOG_SYSLOG]);
+	_log_target_state_set(&conf[QB_LOG_SYSLOG], QB_LOG_STATE_ENABLED);
+	(void)qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
+				QB_LOG_FILTER_FILE, "*", priority);
+}
+
+void
+qb_log_fini(void)
+{
+	struct qb_log_target *t;
+	struct qb_log_filter *flt;
+	struct callsite_section *s;
+	struct qb_list_head *iter;
+	struct qb_list_head *iter2;
+	struct qb_list_head *next;
+	struct qb_list_head *next2;
+	int32_t pos;
+
+	if (!logger_inited) {
+		return;
+	}
+	logger_inited = QB_FALSE;
+	qb_log_thread_stop();
+	pthread_rwlock_destroy(&_listlock);
+
+	for (pos = 0; pos <= conf_active_max; pos++) {
+		t = &conf[pos];
+		_log_target_disable(t);
+		qb_list_for_each_safe(iter2, next2, &t->filter_head) {
+			flt = qb_list_entry(iter2, struct qb_log_filter, list);
+			qb_list_del(iter2);
+			_log_free_filter(flt);
+		}
+	}
+	qb_log_format_fini();
+	qb_log_dcs_fini();
+	qb_list_for_each_safe(iter, next, &callsite_sections) {
+		s = qb_list_entry(iter, struct callsite_section, list);
+		qb_list_del(iter);
+		free(s);
+	}
+	qb_list_for_each_safe(iter, next, &tags_head) {
+		flt = qb_list_entry(iter, struct qb_log_filter, list);
+		qb_list_del(iter);
+		_log_free_filter(flt);
+	}
+}
+
+struct qb_log_target *
+qb_log_target_alloc(void)
+{
+	int32_t i;
+	for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
+		if (conf[i].state == QB_LOG_STATE_UNUSED) {
+			_log_target_state_set(&conf[i], QB_LOG_STATE_DISABLED);
+			return &conf[i];
+		}
+	}
+	return NULL;
+}
+
+void
+qb_log_target_free(struct qb_log_target *t)
+{
+	(void)qb_log_filter_ctl(t->pos, QB_LOG_FILTER_CLEAR_ALL,
+				QB_LOG_FILTER_FILE, NULL, 0);
+	t->debug = QB_FALSE;
+	t->filename[0] = '\0';
+	qb_log_format_set(t->pos, NULL);
+	_log_target_state_set(t, QB_LOG_STATE_UNUSED);
+}
+
+struct qb_log_target *
+qb_log_target_get(int32_t pos)
+{
+	return &conf[pos];
+}
+
+void *
+qb_log_target_user_data_get(int32_t t)
+{
+	if (t < 0 || t >= QB_LOG_TARGET_MAX ||
+	    conf[t].state == QB_LOG_STATE_UNUSED) {
+		errno = -EBADF;
+		return NULL;
+	}
+
+	return conf[t].instance;
+}
+
+int32_t
+qb_log_target_user_data_set(int32_t t, void *user_data)
+{
+	if (!logger_inited) {
+		return -EINVAL;
+	}
+	if (t < 0 || t >= QB_LOG_TARGET_MAX ||
+	    conf[t].state == QB_LOG_STATE_UNUSED) {
+		return -EBADF;
+	}
+
+	conf[t].instance = user_data;
+	return 0;
+}
+
+int32_t
+qb_log_custom_open(qb_log_logger_fn log_fn,
+		   qb_log_close_fn close_fn,
+		   qb_log_reload_fn reload_fn, void *user_data)
+{
+	struct qb_log_target *t;
+
+	t = qb_log_target_alloc();
+	if (t == NULL) {
+		return -errno;
+	}
+
+	t->instance = user_data;
+	snprintf(t->filename, PATH_MAX, "custom-%d", t->pos);
+
+	t->logger = log_fn;
+	t->vlogger = NULL;
+	t->reload = reload_fn;
+	t->close = close_fn;
+
+	return t->pos;
+}
+
+void
+qb_log_custom_close(int32_t t)
+{
+	struct qb_log_target *target;
+
+	if (!logger_inited) {
+		return;
+	}
+	if (t < 0 || t >= QB_LOG_TARGET_MAX ||
+	    conf[t].state == QB_LOG_STATE_UNUSED) {
+		return;
+	}
+
+	target = qb_log_target_get(t);
+
+	if (target->close) {
+		in_logger = QB_TRUE;
+		target->close(t);
+		in_logger = QB_FALSE;
+	}
+	qb_log_target_free(target);
+}
+
+static int32_t
+_log_target_enable(struct qb_log_target *t)
+{
+	int32_t rc = 0;
+
+	if (t->state == QB_LOG_STATE_ENABLED) {
+		return 0;
+	}
+	if (t->pos == QB_LOG_STDERR ||
+	    t->pos == QB_LOG_STDOUT) {
+		rc = qb_log_stderr_open(t);
+	} else if (t->pos == QB_LOG_SYSLOG) {
+		rc = qb_log_syslog_open(t);
+	} else if (t->pos == QB_LOG_BLACKBOX) {
+		rc = qb_log_blackbox_open(t);
+	}
+	if (rc == 0) {
+		_log_target_state_set(t, QB_LOG_STATE_ENABLED);
+	}
+	return rc;
+}
+
+static void
+_log_target_disable(struct qb_log_target *t)
+{
+	if (t->state != QB_LOG_STATE_ENABLED) {
+		return;
+	}
+	_log_target_state_set(t, QB_LOG_STATE_DISABLED);
+	if (t->close) {
+		in_logger = QB_TRUE;
+		t->close(t->pos);
+		in_logger = QB_FALSE;
+	}
+}
+
+int32_t
+qb_log_ctl(int32_t t, enum qb_log_conf c, int32_t arg)
+{
+	int32_t rc = 0;
+	int32_t need_reload = QB_FALSE;
+
+	if (!logger_inited) {
+		return -EINVAL;
+	}
+	if (t < 0 || t >= QB_LOG_TARGET_MAX ||
+	    conf[t].state == QB_LOG_STATE_UNUSED) {
+		return -EBADF;
+	}
+	switch (c) {
+	case QB_LOG_CONF_ENABLED:
+		if (arg) {
+			rc = _log_target_enable(&conf[t]);
+		} else {
+			_log_target_disable(&conf[t]);
+		}
+		break;
+	case QB_LOG_CONF_STATE_GET:
+		rc = conf[t].state;
+		break;
+	case QB_LOG_CONF_FACILITY:
+		conf[t].facility = arg;
+		if (t == QB_LOG_SYSLOG) {
+			need_reload = QB_TRUE;
+		}
+		break;
+	case QB_LOG_CONF_FILE_SYNC:
+		conf[t].file_sync = arg;
+		break;
+	case QB_LOG_CONF_PRIORITY_BUMP:
+		conf[t].priority_bump = arg;
+		break;
+	case QB_LOG_CONF_SIZE:
+		if (t == QB_LOG_BLACKBOX) {
+			if (arg <= 0) {
+				return -EINVAL;
+			}
+			conf[t].size = arg;
+			need_reload = QB_TRUE;
+		} else {
+			return -ENOSYS;
+		}
+		break;
+	case QB_LOG_CONF_THREADED:
+		conf[t].threaded = arg;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+	if (rc == 0 && need_reload && conf[t].reload) {
+		in_logger = QB_TRUE;
+		conf[t].reload(t);
+		in_logger = QB_FALSE;
+	}
+	return rc;
+}
diff --git a/lib/log_blackbox.c b/lib/log_blackbox.c
new file mode 100644
index 0000000..cac6787
--- /dev/null
+++ b/lib/log_blackbox.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbrb.h>
+#include "util_int.h"
+#include "log_int.h"
+
+#define BB_MIN_ENTRY_SIZE (4 * sizeof(uint32_t) +\
+			   sizeof(uint8_t) +\
+			   2 * sizeof(char) + sizeof(time_t))
+
+
+static void
+_blackbox_reload(int32_t target)
+{
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	if (t->instance == NULL) {
+		return;
+	}
+	qb_rb_close(t->instance);
+	t->instance = qb_rb_open(t->filename, t->size,
+				 QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
+}
+
+/* <u32> file lineno
+ * <u32> tags
+ * <u8> priority
+ * <u32> function name length
+ * <string> function name
+ * <u32> buffer length
+ * <string> buffer
+ */
+static void
+_blackbox_vlogger(int32_t target,
+		  struct qb_log_callsite *cs, time_t timestamp, va_list ap)
+{
+	size_t max_size;
+	size_t actual_size;
+	uint32_t fn_size;
+	char *chunk;
+	char *msg_len_pt;
+	uint32_t msg_len;
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	if (t->instance == NULL) {
+		return;
+	}
+
+	fn_size = strlen(cs->function) + 1;
+
+	actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(time_t);
+	max_size = actual_size + QB_LOG_MAX_LEN;
+
+	chunk = qb_rb_chunk_alloc(t->instance, max_size);
+
+	if (chunk == NULL) {
+		/* something bad has happened. abort blackbox logging */
+		qb_util_perror(LOG_ERR, "Blackbox allocation error, aborting blackbox log %s", t->filename);
+		qb_rb_close(t->instance);
+		t->instance = NULL;
+		return;
+	}
+
+	/* line number */
+	memcpy(chunk, &cs->lineno, sizeof(uint32_t));
+	chunk += sizeof(uint32_t);
+
+	/* tags */
+	memcpy(chunk, &cs->tags, sizeof(uint32_t));
+	chunk += sizeof(uint32_t);
+
+	/* log level/priority */
+	memcpy(chunk, &cs->priority, sizeof(uint8_t));
+	chunk += sizeof(uint8_t);
+
+	/* function name */
+	memcpy(chunk, &fn_size, sizeof(uint32_t));
+	chunk += sizeof(uint32_t);
+	memcpy(chunk, cs->function, fn_size);
+	chunk += fn_size;
+
+	/* timestamp */
+	memcpy(chunk, &timestamp, sizeof(time_t));
+	chunk += sizeof(time_t);
+
+	/* log message length */
+	msg_len_pt = chunk;
+	chunk += sizeof(uint32_t);
+
+	/* log message */
+	msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN, cs->format, ap);
+	if (msg_len >= QB_LOG_MAX_LEN) {
+	    chunk = msg_len_pt + sizeof(uint32_t); /* Reset */
+
+	    msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN,
+		"Log message too long to be stored in the blackbox.  "\
+		"Maximum is QB_LOG_MAX_LEN" , ap);
+	    actual_size += msg_len;
+	}
+
+	actual_size += msg_len;
+
+	/* now that we know the length, write it
+	 */
+	memcpy(msg_len_pt, &msg_len, sizeof(uint32_t));
+
+	(void)qb_rb_chunk_commit(t->instance, actual_size);
+}
+
+static void
+_blackbox_close(int32_t target)
+{
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	if (t->instance) {
+		qb_rb_close(t->instance);
+		t->instance = NULL;
+	}
+}
+
+int32_t
+qb_log_blackbox_open(struct qb_log_target *t)
+{
+	if (t->size < 1024) {
+		return -EINVAL;
+	}
+	snprintf(t->filename, PATH_MAX, "%s-%d-blackbox", t->name, getpid());
+
+	t->instance = qb_rb_open(t->filename, t->size,
+				 QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
+	if (t->instance == NULL) {
+		return -errno;
+	}
+
+	t->logger = NULL;
+	t->vlogger = _blackbox_vlogger;
+	t->reload = _blackbox_reload;
+	t->close = _blackbox_close;
+	return 0;
+}
+
+ssize_t
+qb_log_blackbox_write_to_file(const char *filename)
+{
+	ssize_t written_size = 0;
+	struct qb_log_target *t;
+	int fd = open(filename, O_CREAT | O_RDWR, 0700);
+
+	if (fd < 0) {
+		return -errno;
+	}
+	t = qb_log_target_get(QB_LOG_BLACKBOX);
+	if (t->instance) {
+		written_size = qb_rb_write_to_file(t->instance, fd);
+	} else {
+		written_size = -ENOENT;
+	}
+	close(fd);
+
+	return written_size;
+}
+
+void
+qb_log_blackbox_print_from_file(const char *bb_filename)
+{
+	qb_ringbuffer_t *instance;
+	ssize_t bytes_read;
+	int max_size = 2 * QB_LOG_MAX_LEN;
+	char *chunk;
+	int fd;
+	char time_buf[64];
+
+	fd = open(bb_filename, 0);
+	if (fd < 0) {
+		qb_util_perror(LOG_ERR, "qb_log_blackbox_print_from_file");
+		return;
+	}
+	instance = qb_rb_create_from_file(fd, 0);
+	close(fd);
+	if (instance == NULL) {
+		return;
+	}
+	chunk = malloc(max_size);
+
+	do {
+		char *ptr;
+		uint32_t lineno;
+		uint32_t tags;
+		uint8_t priority;
+		uint32_t fn_size;
+		char *function;
+		uint32_t len;
+		time_t timestamp;
+		uint32_t msg_len;
+		struct tm *tm;
+		char message[QB_LOG_MAX_LEN];
+
+		bytes_read = qb_rb_chunk_read(instance, chunk, max_size, 0);
+
+		if (bytes_read >= 0 && bytes_read < BB_MIN_ENTRY_SIZE) {
+			printf("ERROR Corrupt file: blackbox header too small.\n");
+			goto cleanup;
+		} else if (bytes_read < 0) {
+			errno = -bytes_read;
+			perror("ERROR: qb_rb_chunk_read failed");
+			goto cleanup;
+		}
+		ptr = chunk;
+
+		/* lineno */
+		memcpy(&lineno, ptr, sizeof(uint32_t));
+		ptr += sizeof(uint32_t);
+
+		/* tags */
+		memcpy(&tags, ptr, sizeof(uint32_t));
+		ptr += sizeof(uint32_t);
+
+		/* priority */
+		memcpy(&priority, ptr, sizeof(uint8_t));
+		ptr += sizeof(uint8_t);
+
+		/* function size & name */
+		memcpy(&fn_size, ptr, sizeof(uint32_t));
+		if ((fn_size + BB_MIN_ENTRY_SIZE) > bytes_read) {
+			printf("ERROR Corrupt file: fn_size way too big %d\n", fn_size);
+			goto cleanup;
+		}
+		if (fn_size <= 0) {
+			printf("ERROR Corrupt file: fn_size negative %d\n", fn_size);
+			goto cleanup;
+		}
+		ptr += sizeof(uint32_t);
+
+		function = ptr;
+		ptr += fn_size;
+
+		/* timestamp size & content */
+		memcpy(&timestamp, ptr, sizeof(time_t));
+		ptr += sizeof(time_t);
+		tm = localtime(&timestamp);
+		if (tm) {
+			(void)strftime(time_buf,
+				       sizeof(time_buf), "%b %d %T",
+				       tm);
+		} else {
+			snprintf(time_buf, sizeof(time_buf), "%ld",
+				 (long int)timestamp);
+		}
+		/* message length */
+		memcpy(&msg_len, ptr, sizeof(uint32_t));
+		if (msg_len > QB_LOG_MAX_LEN || msg_len <= 0) {
+			printf("ERROR Corrupt file: msg_len out of bounds %d\n", msg_len);
+			goto cleanup;
+		}
+
+		ptr += sizeof(uint32_t);
+
+		/* message content */
+		len = qb_vsnprintf_deserialize(message, QB_LOG_MAX_LEN, ptr);
+		assert(len > 0);
+		message[len] = '\0';
+		len--;
+		while (len > 0 && (message[len] == '\n' || message[len] == '\0')) {
+			message[len] = '\0';
+			len--;
+		}
+
+		printf("%-7s %s %s(%u):%u: %s\n",
+		       qb_log_priority2str(priority),
+		       time_buf, function, lineno, tags, message);
+
+	} while (bytes_read > BB_MIN_ENTRY_SIZE);
+
+cleanup:
+	qb_rb_close(instance);
+	free(chunk);
+}
diff --git a/lib/log_dcs.c b/lib/log_dcs.c
new file mode 100644
index 0000000..0249e6d
--- /dev/null
+++ b/lib/log_dcs.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <ctype.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+#include <stdarg.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <qb/qbutil.h>
+#include <qb/qbarray.h>
+#include "log_int.h"
+
+static qb_array_t *lookup_arr = NULL;
+static qb_array_t *callsite_arr = NULL;
+static uint32_t callsite_arr_next = 0;
+static uint32_t callsite_elems_per_bin = 0;
+static qb_thread_lock_t *arr_next_lock = NULL;
+
+struct callsite_list {
+	struct qb_log_callsite *cs;
+	struct callsite_list *next;
+};
+
+static void
+_log_register_callsites(qb_array_t * a, uint32_t bin)
+{
+	struct qb_log_callsite *start;
+	struct qb_log_callsite *stop;
+	int32_t rc = qb_array_index(callsite_arr,
+				    bin * callsite_elems_per_bin,
+				    (void **)&start);
+	if (rc == 0) {
+		stop = &start[callsite_elems_per_bin];
+		rc = qb_log_callsites_register(start, stop);
+		assert(rc == 0);
+	}
+}
+
+static struct qb_log_callsite *
+_log_dcs_new_cs(const char *function,
+		const char *filename,
+		const char *format,
+		uint8_t priority, uint32_t lineno, uint32_t tags)
+{
+	struct qb_log_callsite *cs;
+	int32_t rc = qb_array_index(callsite_arr,
+				    callsite_arr_next++,
+				    (void **)&cs);
+	assert(rc == 0);
+	assert(cs != NULL);
+
+	cs->function = strdup(function);
+	cs->filename = strdup(filename);
+	cs->format = strdup(format);
+	cs->priority = priority;
+	cs->lineno = lineno;
+	cs->tags = tags;
+
+	return cs;
+}
+
+struct qb_log_callsite *
+qb_log_dcs_get(int32_t * newly_created,
+	       const char *function,
+	       const char *filename,
+	       const char *format,
+	       uint8_t priority, uint32_t lineno, uint32_t tags)
+{
+	int32_t rc;
+	struct qb_log_callsite *cs = NULL;
+	struct callsite_list *csl_head;
+	struct callsite_list *csl_last = NULL;
+	struct callsite_list *csl;
+	const char *safe_filename = filename;
+	const char *safe_function = function;
+	const char *safe_format = format;
+
+	if (filename == NULL) {
+		safe_filename = "";
+	}
+	if (function == NULL) {
+		safe_function = "";
+	}
+	if (format == NULL) {
+		safe_format = "";
+	}
+	/*
+	 * try the fastest access first (no locking needed)
+	 */
+	rc = qb_array_index(lookup_arr, lineno, (void **)&csl_head);
+	assert(rc == 0);
+	if (csl_head->cs &&
+		priority == csl_head->cs->priority &&
+		strcmp(safe_filename, csl_head->cs->filename) == 0 &&
+		strcmp(safe_format, csl_head->cs->format) == 0) {
+		return csl_head->cs;
+	}
+	/*
+	 * so we will either have to create it or go through a list, so lock it.
+	 */
+	(void)qb_thread_lock(arr_next_lock);
+	if (csl_head->cs == NULL) {
+		csl_head->cs = _log_dcs_new_cs(safe_function, safe_filename, safe_format,
+					       priority, lineno, tags);
+		cs = csl_head->cs;
+		csl_head->next = NULL;
+		*newly_created = QB_TRUE;
+	} else {
+		for (csl = csl_head; csl; csl = csl->next) {
+			assert(csl->cs->lineno == lineno);
+			if (priority == csl->cs->priority &&
+			    strcmp(safe_format, csl->cs->format) == 0 &&
+			    strcmp(safe_filename, csl->cs->filename) == 0) {
+				cs = csl->cs;
+				break;
+			}
+			csl_last = csl;
+		}
+
+		if (cs == NULL) {
+			csl = calloc(1, sizeof(struct callsite_list));
+			if (csl == NULL) {
+				goto cleanup;
+			}
+			csl->cs = _log_dcs_new_cs(safe_function, safe_filename, safe_format,
+						  priority, lineno, tags);
+			csl->next = NULL;
+			csl_last->next = csl;
+			cs = csl->cs;
+			*newly_created = QB_TRUE;
+		}
+	}
+cleanup:
+	(void)qb_thread_unlock(arr_next_lock);
+
+	return cs;
+}
+
+void
+qb_log_dcs_init(void)
+{
+	int32_t rc;
+
+	lookup_arr = qb_array_create_2(16, sizeof(struct callsite_list), 1);
+	callsite_arr = qb_array_create_2(16, sizeof(struct qb_log_callsite), 1);
+
+	arr_next_lock = qb_thread_lock_create(QB_THREAD_LOCK_SHORT);
+
+	callsite_elems_per_bin = qb_array_elems_per_bin_get(callsite_arr);
+	rc = qb_array_new_bin_cb_set(callsite_arr, _log_register_callsites);
+	assert(rc == 0);
+}
+
+void
+qb_log_dcs_fini(void)
+{
+	struct callsite_list *csl_head;
+	struct callsite_list *csl_next;
+	struct callsite_list *csl;
+	int32_t i;
+	int32_t rc;
+	struct qb_log_callsite *cs = NULL;
+	int32_t cnt = qb_array_num_bins_get(lookup_arr);
+	cnt *= qb_array_elems_per_bin_get(lookup_arr);
+
+	for (i = 0; i < cnt; i++) {
+		rc = qb_array_index(lookup_arr, i, (void **)&csl_head);
+		if (rc != 0 || csl_head->next == NULL) {
+			continue;
+		}
+
+		for (csl = csl_head->next; csl; csl = csl_next) {
+			csl_next = csl->next;
+			free(csl);
+		}
+	}
+
+	for (i = 0; i < callsite_arr_next; i++) {
+		rc = qb_array_index(callsite_arr, i, (void **)&cs);
+		if (rc == 0 && cs){
+			free((char*)cs->function);
+			free((char*)cs->filename);
+			free((char*)cs->format);
+		}
+	}
+
+	qb_array_free(lookup_arr);
+	qb_array_free(callsite_arr);
+	(void)qb_thread_lock_destroy(arr_next_lock);
+}
diff --git a/lib/log_file.c b/lib/log_file.c
new file mode 100644
index 0000000..8f1a94b
--- /dev/null
+++ b/lib/log_file.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include "log_int.h"
+
+static void
+_file_logger(int32_t t,
+	     struct qb_log_callsite *cs, time_t timestamp, const char *msg)
+{
+	char output_buffer[QB_LOG_MAX_LEN];
+	struct qb_log_target *target = qb_log_target_get(t);
+	FILE *f = qb_log_target_user_data_get(t);
+
+	if (f == NULL) {
+		return;
+	}
+	output_buffer[0] = '\0';
+
+	qb_log_target_format(t, cs, timestamp, msg, output_buffer);
+
+	fprintf(f, "%s\n", output_buffer);
+
+	fflush(f);
+	if (target->file_sync) {
+		fsync(fileno(f));
+	}
+}
+
+static void
+_file_close(int32_t t)
+{
+	FILE *f = qb_log_target_user_data_get(t);
+
+	if (f) {
+		fclose(f);
+		(void)qb_log_target_user_data_set(t, NULL);
+	}
+}
+
+static void
+_file_reload(int32_t target)
+{
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	if (t->instance) {
+		fclose(t->instance);
+	}
+	t->instance = fopen(t->filename, "a+");
+}
+
+int32_t
+qb_log_stderr_open(struct qb_log_target *t)
+{
+	t->logger = _file_logger;
+	t->reload = NULL;
+	t->close = NULL;
+	if (t->pos == QB_LOG_STDERR) {
+		(void)strlcpy(t->filename, "stderr", PATH_MAX);
+		t->instance = stderr;
+	} else {
+		(void)strlcpy(t->filename, "stdout", PATH_MAX);
+		t->instance = stdout;
+	}
+	return 0;
+}
+
+int32_t
+qb_log_file_open(const char *filename)
+{
+	struct qb_log_target *t;
+	FILE *fp;
+	int32_t rc;
+
+	t = qb_log_target_alloc();
+	if (t == NULL) {
+		return -errno;
+	}
+
+	fp = fopen(filename, "a+");
+	if (fp == NULL) {
+		rc = -errno;
+		qb_log_target_free(t);
+		return rc;
+	}
+	t->instance = fp;
+	(void)strlcpy(t->filename, filename, PATH_MAX);
+
+	t->logger = _file_logger;
+	t->reload = _file_reload;
+	t->close = _file_close;
+	return t->pos;
+}
+
+void
+qb_log_file_close(int32_t t)
+{
+	qb_log_custom_close(t);
+}
diff --git a/lib/log_format.c b/lib/log_format.c
new file mode 100644
index 0000000..a8b3239
--- /dev/null
+++ b/lib/log_format.c
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <ctype.h>
+
+#include <qb/qbdefs.h>
+#include "log_int.h"
+
+static qb_log_tags_stringify_fn _user_tags_stringify_fn;
+
+/*
+ * syslog prioritynames, facility names to value mapping
+ * Some C libraries build this in to their headers, but it is non-portable
+ * so logsys supplies its own version.
+ */
+struct syslog_names {
+	const char *c_name;
+	int32_t c_val;
+};
+
+static struct syslog_names prioritynames[] = {
+	{"emerg", LOG_EMERG},
+	{"alert", LOG_ALERT},
+	{"crit", LOG_CRIT},
+	{"error", LOG_ERR},
+	{"warning", LOG_WARNING},
+	{"notice", LOG_NOTICE},
+	{"info", LOG_INFO},
+	{"debug", LOG_DEBUG},
+	{"trace", LOG_TRACE},
+	{NULL, -1}
+};
+
+struct syslog_names facilitynames[] = {
+	{"auth", LOG_AUTH},
+#if defined(LOG_AUTHPRIV)
+	{"authpriv", LOG_AUTHPRIV},
+#endif
+	{"cron", LOG_CRON},
+	{"daemon", LOG_DAEMON},
+#if defined(LOG_FTP)
+	{"ftp", LOG_FTP},
+#endif
+	{"kern", LOG_KERN},
+	{"lpr", LOG_LPR},
+	{"mail", LOG_MAIL},
+	{"news", LOG_NEWS},
+	{"syslog", LOG_SYSLOG},
+	{"user", LOG_USER},
+	{"uucp", LOG_UUCP},
+	{"local0", LOG_LOCAL0},
+	{"local1", LOG_LOCAL1},
+	{"local2", LOG_LOCAL2},
+	{"local3", LOG_LOCAL3},
+	{"local4", LOG_LOCAL4},
+	{"local5", LOG_LOCAL5},
+	{"local6", LOG_LOCAL6},
+	{"local7", LOG_LOCAL7},
+	{NULL, -1}
+};
+
+static const char log_month_name[][4] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static pthread_rwlock_t _formatlock;
+
+void
+qb_log_format_init(void)
+{
+	int32_t i;
+	struct qb_log_target *t;
+
+	i = pthread_rwlock_init(&_formatlock, NULL);
+	assert(i == 0);
+
+	for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
+		t = qb_log_target_get(i);
+		t->format = strdup("[%p] %b");
+	}
+}
+
+void
+qb_log_format_fini(void)
+{
+	struct qb_log_target *t;
+	int32_t i;
+
+	pthread_rwlock_destroy(&_formatlock);
+
+	for (i = 0; i < QB_LOG_TARGET_MAX; i++) {
+		t = qb_log_target_get(i);
+		free(t->format);
+	}
+}
+
+void
+qb_log_format_set(int32_t target, const char *format)
+{
+	char modified_format[256];
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	pthread_rwlock_wrlock(&_formatlock);
+
+	free(t->format);
+
+	if (format) {
+		qb_log_target_format_static(target, format, modified_format);
+		t->format = strdup(modified_format);
+	} else {
+		t->format = strdup("[%p] %b");
+	}
+	assert(t->format != NULL);
+
+	pthread_rwlock_unlock(&_formatlock);
+}
+
+/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
+int32_t
+qb_log_facility2int(const char *fname)
+{
+	int32_t i;
+
+	if (fname == NULL) {
+		return -EINVAL;
+	}
+
+	for (i = 0; facilitynames[i].c_name != NULL; i++) {
+		if (strcmp(fname, facilitynames[i].c_name) == 0) {
+			return facilitynames[i].c_val;
+		}
+	}
+	return -EINVAL;
+}
+
+/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
+const char *
+qb_log_facility2str(int32_t fnum)
+{
+	int32_t i;
+
+	for (i = 0; facilitynames[i].c_name != NULL; i++) {
+		if (facilitynames[i].c_val == fnum) {
+			return facilitynames[i].c_name;
+		}
+	}
+	return NULL;
+}
+
+const char *
+qb_log_priority2str(uint8_t priority)
+{
+	if (priority > LOG_TRACE) {
+		return prioritynames[LOG_TRACE].c_name;
+	}
+	return prioritynames[priority].c_name;
+}
+
+void
+qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn)
+{
+	_user_tags_stringify_fn = fn;
+}
+
+static int
+_strcpy_cutoff(char *dest, const char *src, size_t cutoff, int ralign,
+	       size_t buf_len)
+{
+	size_t len = strlen(src);
+	if (buf_len <= 1) {
+		if (buf_len == 0)
+			dest[0] = 0;
+		return 0;
+	}
+
+	if (cutoff == 0) {
+		cutoff = len;
+	}
+
+	cutoff = QB_MIN(cutoff, buf_len - 1);
+	len = QB_MIN(len, cutoff);
+	if (ralign) {
+		memset(dest, ' ', cutoff - len);
+		memcpy(dest + cutoff - len, src, len);
+	} else {
+		memcpy(dest, src, len);
+		memset(dest + len, ' ', cutoff - len);
+	}
+
+	dest[cutoff] = '\0';
+
+	return cutoff;
+}
+
+/*
+ * This function will do static formatting (for things that don't
+ * change on each log message).
+ *
+ * %P PID
+ * %N name passed into qb_log_init
+ * %H hostname
+ *
+ * any number between % and character specify field length to pad or chop
+ */
+void
+qb_log_target_format_static(int32_t target, const char * format,
+			    char *output_buffer)
+{
+	char tmp_buf[255];
+	unsigned int format_buffer_idx = 0;
+	unsigned int output_buffer_idx = 0;
+	size_t cutoff;
+	uint32_t len;
+	int ralign;
+	int c;
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	if (format == NULL) {
+		return;
+	}
+
+	while ((c = format[format_buffer_idx])) {
+		cutoff = 0;
+		ralign = QB_FALSE;
+		if (c != '%') {
+			output_buffer[output_buffer_idx++] = c;
+			format_buffer_idx++;
+		} else {
+			const char *p;
+			unsigned int percent_buffer_idx = format_buffer_idx;
+
+			format_buffer_idx += 1;
+			if (format[format_buffer_idx] == '-') {
+				ralign = QB_TRUE;
+				format_buffer_idx += 1;
+			}
+
+			if (isdigit(format[format_buffer_idx])) {
+				cutoff = atoi(&format[format_buffer_idx]);
+			}
+			while (isdigit(format[format_buffer_idx])) {
+				format_buffer_idx += 1;
+			}
+
+			switch (format[format_buffer_idx]) {
+			case 'P':
+				snprintf(tmp_buf, 30, "%d", getpid());
+				p = tmp_buf;
+				break;
+
+			case 'N':
+				p = t->name;
+				break;
+
+			case 'H':
+				if (gethostname(tmp_buf, 255) == 0) {
+					tmp_buf[254] = '\0';
+				} else {
+					(void)strlcpy(tmp_buf, "localhost", 255);
+				}
+				p = tmp_buf;
+				break;
+
+			default:
+				p = &format[percent_buffer_idx];
+				cutoff = (format_buffer_idx - percent_buffer_idx + 1);
+				ralign = QB_FALSE;
+				break;
+			}
+			len = _strcpy_cutoff(output_buffer + output_buffer_idx,
+					     p, cutoff, ralign,
+					     (QB_LOG_MAX_LEN -
+					      output_buffer_idx));
+			output_buffer_idx += len;
+			format_buffer_idx += 1;
+		}
+		if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
+			break;
+		}
+	}
+
+	output_buffer[output_buffer_idx] = '\0';
+}
+
+/*
+ * %n FUNCTION NAME
+ * %f FILENAME
+ * %l FILELINE
+ * %p PRIORITY
+ * %t TIMESTAMP
+ * %b BUFFER
+ * %g SUBSYSTEM
+ *
+ * any number between % and character specify field length to pad or chop
+ */
+void
+qb_log_target_format(int32_t target,
+		     struct qb_log_callsite *cs,
+		     time_t current_time,
+		     const char *formatted_message, char *output_buffer)
+{
+	char tmp_buf[128];
+	struct tm tm_res;
+	unsigned int format_buffer_idx = 0;
+	unsigned int output_buffer_idx = 0;
+	size_t cutoff;
+	uint32_t len;
+	int ralign;
+	int c;
+	struct qb_log_target *t = qb_log_target_get(target);
+
+	pthread_rwlock_rdlock(&_formatlock);
+	if (t->format == NULL) {
+		pthread_rwlock_unlock(&_formatlock);
+		return;
+	}
+
+	while ((c = t->format[format_buffer_idx])) {
+		cutoff = 0;
+		ralign = QB_FALSE;
+		if (c != '%') {
+			output_buffer[output_buffer_idx++] = c;
+			format_buffer_idx++;
+		} else {
+			const char *p;
+
+			format_buffer_idx += 1;
+			if (t->format[format_buffer_idx] == '-') {
+				ralign = QB_TRUE;
+				format_buffer_idx += 1;
+			}
+
+			if (isdigit(t->format[format_buffer_idx])) {
+				cutoff = atoi(&t->format[format_buffer_idx]);
+			}
+			while (isdigit(t->format[format_buffer_idx])) {
+				format_buffer_idx += 1;
+			}
+
+			switch (t->format[format_buffer_idx]) {
+			case 'g':
+				if (_user_tags_stringify_fn) {
+					p = _user_tags_stringify_fn(cs->tags);
+				} else {
+					p = "";
+				}
+				break;
+
+			case 'n':
+				p = cs->function;
+				break;
+
+			case 'f':
+#ifdef BUILDING_IN_PLACE
+				p = cs->filename;
+#else
+				p = strrchr(cs->filename, '/');
+				if (p == NULL) {
+					p = cs->filename;
+				} else {
+					p++; /* move past the "/" */
+				}
+#endif /* BUILDING_IN_PLACE */
+				break;
+
+			case 'l':
+				snprintf(tmp_buf, 30, "%d", cs->lineno);
+				p = tmp_buf;
+				break;
+
+			case 't':
+				(void)localtime_r(&current_time, &tm_res);
+				snprintf(tmp_buf, TIME_STRING_SIZE,
+					 "%s %02d %02d:%02d:%02d",
+					 log_month_name[tm_res.tm_mon],
+					 tm_res.tm_mday, tm_res.tm_hour,
+					 tm_res.tm_min, tm_res.tm_sec);
+				p = tmp_buf;
+				break;
+
+			case 'b':
+				p = formatted_message;
+				break;
+
+			case 'p':
+				if (cs->priority > LOG_TRACE) {
+					p = prioritynames[LOG_TRACE].c_name;
+				} else {
+					p = prioritynames[cs->priority].c_name;
+				}
+				break;
+
+			default:
+				p = "";
+				break;
+			}
+			len = _strcpy_cutoff(output_buffer + output_buffer_idx,
+					     p, cutoff, ralign,
+					     (QB_LOG_MAX_LEN -
+					      output_buffer_idx));
+			output_buffer_idx += len;
+			format_buffer_idx += 1;
+		}
+		if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
+			break;
+		}
+	}
+	pthread_rwlock_unlock(&_formatlock);
+
+	if (output_buffer[output_buffer_idx - 1] == '\n') {
+		output_buffer[output_buffer_idx - 1] = '\0';
+	} else {
+		output_buffer[output_buffer_idx] = '\0';
+	}
+}
+
+
+/*
+ * These wrappers around strl* functions just return the
+ * number of characters written, not the number of characters
+ * requested to be written.
+ */
+static size_t
+my_strlcpy(char *dest, const char * src, size_t maxlen)
+{
+	size_t rc = strlcpy(dest, src, maxlen);
+	/* maxlen includes NUL, so -1 */
+	return QB_MIN(rc, maxlen-1);
+}
+
+static size_t
+my_strlcat(char *dest, const char * src, size_t maxlen)
+{
+	size_t rc = strlcat(dest, src, maxlen);
+	return QB_MIN(rc, maxlen-1);
+}
+
+size_t
+qb_vsnprintf_serialize(char *serialize, size_t max_len,
+		       const char *fmt, va_list ap)
+{
+	char *format;
+	char *p;
+	int type_long = QB_FALSE;
+	int type_longlong = QB_FALSE;
+        int sformat_length = 0;
+        int sformat_precision = QB_FALSE;
+	uint32_t location = my_strlcpy(serialize, fmt, max_len) + 1;
+
+	format = (char *)fmt;
+	for (;;) {
+		type_long = QB_FALSE;
+		type_longlong = QB_FALSE;
+		p = strchrnul((const char *)format, '%');
+		if (*p == '\0') {
+			break;
+		}
+		format = p + 1;
+reprocess:
+		switch (format[0]) {
+		case '#': /* alternate form conversion, ignore */
+		case '-': /* left adjust, ignore */
+		case ' ': /* a space, ignore */
+		case '+': /* a sign should be used, ignore */
+		case '\'': /* group in thousands, ignore */
+		case 'I': /* glibc-ism locale alternative, ignore */
+                    format++;
+                    goto reprocess;
+		case '.': /* precision, ignore */
+                    format++;
+                    sformat_precision = QB_TRUE;
+                    goto reprocess;
+		case '0': /* field width, ignore */
+		case '1': /* field width, ignore */
+		case '2': /* field width, ignore */
+		case '3': /* field width, ignore */
+		case '4': /* field width, ignore */
+		case '5': /* field width, ignore */
+		case '6': /* field width, ignore */
+		case '7': /* field width, ignore */
+		case '8': /* field width, ignore */
+		case '9': /* field width, ignore */
+                        if (sformat_precision) {
+                            sformat_length *= 10;
+                            sformat_length += (format[0] - '0');
+                        }
+			format++;
+			goto reprocess;
+		case '*': /* variable field width, save */ {
+			int arg_int = va_arg(ap, int);
+			if (location + sizeof (int) > max_len) {
+				return max_len;
+			}
+			memcpy(&serialize[location], &arg_int, sizeof (int));
+			location += sizeof(int);
+			format++;
+			goto reprocess;
+		}
+		case 'l':
+			format++;
+			type_long = QB_TRUE;
+			if (*format == 'l') {
+				type_long = QB_FALSE;
+				type_longlong = QB_TRUE;
+				format++;
+			}
+			goto reprocess;
+		case 'd': /* int argument */
+		case 'i': /* int argument */
+		case 'o': /* unsigned int argument */
+		case 'u':
+		case 'x':
+		case 'X':
+			if (type_long) {
+				long int arg_int;
+
+				if (location + sizeof (long int) > max_len) {
+					return max_len;
+				}
+				arg_int = va_arg(ap, long int);
+				memcpy(&serialize[location], &arg_int,
+				       sizeof(long int));
+				location += sizeof(long int);
+				format++;
+				break;
+			} else if (type_longlong) {
+				long long int arg_int;
+
+				if (location + sizeof (long long int) > max_len) {
+					return max_len;
+				}
+				arg_int = va_arg(ap, long long int);
+				memcpy(&serialize[location], &arg_int,
+				       sizeof(long long int));
+				location += sizeof(long long int);
+				format++;
+				break;
+			} else {
+				int arg_int;
+
+				if (location + sizeof (int) > max_len) {
+					return max_len;
+				}
+				arg_int = va_arg(ap, int);
+				memcpy(&serialize[location], &arg_int,
+				       sizeof(int));
+				location += sizeof(int);
+				format++;
+				break;
+			}
+		case 'e':
+		case 'E':
+		case 'f':
+		case 'F':
+		case 'g':
+		case 'G':
+		case 'a':
+		case 'A':
+			{
+			double arg_double;
+
+			if (location + sizeof (double) > max_len) {
+				return max_len;
+			}
+			arg_double = va_arg(ap, double);
+			memcpy (&serialize[location], &arg_double, sizeof (double));
+			location += sizeof(double);
+			format++;
+			break;
+			}
+		case 'c':
+			{
+			int arg_int;
+			unsigned char arg_char;
+
+			if (location + sizeof (unsigned int) > max_len) {
+				return max_len;
+			}
+			arg_int = va_arg(ap, unsigned int);
+			arg_char = (unsigned char)arg_int;
+			memcpy (&serialize[location], &arg_char, sizeof (unsigned char));
+			location += sizeof(unsigned char);
+			break;
+			}
+		case 's':
+			{
+			char *arg_string;
+			arg_string = va_arg(ap, char *);
+			if (arg_string == NULL) {
+				location += my_strlcpy(&serialize[location],
+						   "(null)",
+						   QB_MIN(strlen("(null)") + 1,
+							  max_len - location));
+			} else if (sformat_length) {
+				location += my_strlcpy(&serialize[location],
+						   arg_string,
+						   QB_MIN(sformat_length + 1,
+						   (max_len - location)));
+			} else {
+				location += my_strlcpy(&serialize[location],
+						   arg_string,
+						   QB_MIN(strlen(arg_string) + 1,
+							  max_len - location));
+			}
+			location++;
+			break;
+			}
+		case 'p':
+			{
+			ptrdiff_t arg_pointer = va_arg(ap, ptrdiff_t);
+			if (location + sizeof (ptrdiff_t) > max_len) {
+				return max_len;
+			}
+			memcpy(&serialize[location], &arg_pointer, sizeof(ptrdiff_t));
+			location += sizeof(ptrdiff_t);
+			break;
+			}
+		case '%':
+			if (location + 1 > max_len) {
+				return max_len;
+			}
+			serialize[location++] = '%';
+                        sformat_length = 0;
+                        sformat_precision = QB_FALSE;
+			break;
+
+		}
+	}
+	return (location);
+}
+
+#define MINI_FORMAT_STR_LEN 20
+
+size_t
+qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf)
+{
+	char *p;
+	char *format;
+	char fmt[MINI_FORMAT_STR_LEN];
+	int fmt_pos;
+
+	uint32_t location = 0;
+	uint32_t data_pos = strlen(buf) + 1;
+	int type_long = QB_FALSE;
+	int type_longlong = QB_FALSE;
+	int len;
+
+	string[0] = '\0';
+	format = (char *)buf;
+	for (;;) {
+		type_long = QB_FALSE;
+		type_longlong = QB_FALSE;
+		p = strchrnul((const char *)format, '%');
+		if (*p == '\0') {
+			return my_strlcat(string, format, str_len) + 1;
+		}
+		/* copy from current to the next % */
+		len = p - format;
+		memcpy(&string[location], format, len);
+		location += len;
+		format = p;
+
+		/* start building up the format for snprintf */
+		fmt_pos = 0;
+		fmt[fmt_pos++] = *format;
+		format++;
+reprocess:
+		switch (format[0]) {
+		case '#': /* alternate form conversion, ignore */
+		case '-': /* left adjust, ignore */
+		case ' ': /* a space, ignore */
+		case '+': /* a sign should be used, ignore */
+		case '\'': /* group in thousands, ignore */
+		case 'I': /* glibc-ism locale alternative, ignore */
+		case '.': /* precision, ignore */
+		case '0': /* field width, ignore */
+		case '1': /* field width, ignore */
+		case '2': /* field width, ignore */
+		case '3': /* field width, ignore */
+		case '4': /* field width, ignore */
+		case '5': /* field width, ignore */
+		case '6': /* field width, ignore */
+		case '7': /* field width, ignore */
+		case '8': /* field width, ignore */
+		case '9': /* field width, ignore */
+			fmt[fmt_pos++] = *format;
+			format++;
+			goto reprocess;
+
+		case '*': {
+			int arg_int;
+			memcpy(&arg_int, &buf[data_pos], sizeof(int));
+			data_pos += sizeof(int);
+			fmt_pos += snprintf(&fmt[fmt_pos],
+					   MINI_FORMAT_STR_LEN - fmt_pos,
+					   "%d", arg_int);
+			format++;
+			goto reprocess;
+		}
+		case 'l':
+			fmt[fmt_pos++] = *format;
+			format++;
+			type_long = QB_TRUE;
+			if (*format == 'l') {
+				type_long = QB_FALSE;
+				type_longlong = QB_TRUE;
+			}
+			goto reprocess;
+		case 'd': /* int argument */
+		case 'i': /* int argument */
+		case 'o': /* unsigned int argument */
+		case 'u':
+		case 'x':
+		case 'X':
+			if (type_long) {
+				long int arg_int;
+
+				fmt[fmt_pos++] = *format;
+				fmt[fmt_pos++] = '\0';
+				memcpy(&arg_int, &buf[data_pos], sizeof(long int));
+				location += snprintf(&string[location],
+						     str_len - location,
+						     fmt, arg_int);
+				data_pos += sizeof(long int);
+				format++;
+				break;
+			} else if (type_longlong) {
+				long long int arg_int;
+
+				fmt[fmt_pos++] = *format;
+				fmt[fmt_pos++] = '\0';
+				memcpy(&arg_int, &buf[data_pos], sizeof(long long int));
+				location += snprintf(&string[location],
+						     str_len - location,
+						     fmt, arg_int);
+				data_pos += sizeof(long long int);
+				format++;
+				break;
+			} else {
+				int arg_int;
+
+				fmt[fmt_pos++] = *format;
+				fmt[fmt_pos++] = '\0';
+				memcpy(&arg_int, &buf[data_pos], sizeof(int));
+				location += snprintf(&string[location],
+						     str_len - location,
+						     fmt, arg_int);
+				data_pos += sizeof(int);
+				format++;
+				break;
+			}
+		case 'e':
+		case 'E':
+		case 'f':
+		case 'F':
+		case 'g':
+		case 'G':
+		case 'a':
+		case 'A':
+			{
+			double arg_double;
+
+			fmt[fmt_pos++] = *format;
+			fmt[fmt_pos++] = '\0';
+			memcpy(&arg_double, &buf[data_pos], sizeof(double));
+			location += snprintf(&string[location],
+					     str_len - location,
+					     fmt, arg_double);
+			data_pos += sizeof(double);
+			format++;
+			break;
+			}
+		case 'c':
+			{
+			unsigned char *arg_char;
+
+			fmt[fmt_pos++] = *format;
+			fmt[fmt_pos++] = '\0';
+			arg_char = (unsigned char*)&buf[data_pos];
+			location += snprintf(&string[location],
+					     str_len - location,
+					     fmt, *arg_char);
+			data_pos += sizeof(unsigned char);
+			format++;
+			break;
+			}
+		case 's':
+			{
+			fmt[fmt_pos++] = *format;
+			fmt[fmt_pos++] = '\0';
+			len = snprintf(&string[location],
+				       str_len - location,
+				       fmt, &buf[data_pos]);
+			location += len;
+			/* don't use len as there might be a len modifier */
+			data_pos += strlen(&buf[data_pos]) + 1;
+			format++;
+			break;
+			}
+		case 'p':
+			{
+			ptrdiff_t pt;
+			memcpy(&pt, &buf[data_pos],
+			       sizeof(ptrdiff_t));
+			fmt[fmt_pos++] = *format;
+			fmt[fmt_pos++] = '\0';
+			location += snprintf(&string[location],
+					     str_len - location,
+					     fmt, pt);
+			data_pos += sizeof(void*);
+			format++;
+			break;
+			}
+		case '%':
+			string[location++] = '%';
+			format++;
+			break;
+
+		}
+	}
+	return location;
+}
+
diff --git a/lib/log_int.h b/lib/log_int.h
new file mode 100644
index 0000000..e25c621
--- /dev/null
+++ b/lib/log_int.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _QB_LOG_INT_H_
+#define _QB_LOG_INT_H_
+
+#include <qb/qblist.h>
+#include <qb/qblog.h>
+#include <qb/qbrb.h>
+#include <regex.h>
+
+struct qb_log_target;
+
+struct qb_log_target {
+	uint32_t pos;
+	enum qb_log_target_state state;
+	char name[PATH_MAX];
+	char filename[PATH_MAX];
+	struct qb_list_head filter_head;
+	int32_t facility;
+	int32_t priority_bump;
+	int32_t file_sync;
+	int32_t debug;
+	size_t size;
+	char *format;
+	int32_t threaded;
+	void *instance;
+
+	qb_log_reload_fn reload;
+	qb_log_close_fn close;
+	qb_log_logger_fn logger;
+	qb_log_vlogger_fn vlogger;
+};
+
+struct qb_log_filter {
+	enum qb_log_filter_conf conf;
+	enum qb_log_filter_type type;
+	char *text;
+	uint8_t high_priority;
+	uint8_t low_priority;
+	uint32_t new_value;
+	struct qb_list_head list;
+	regex_t *regex;
+};
+
+struct qb_log_record {
+	struct qb_log_callsite *cs;
+	time_t timestamp;
+	char *buffer;
+	struct qb_list_head list;
+};
+
+
+#define TIME_STRING_SIZE 64
+
+struct qb_log_target * qb_log_target_alloc(void);
+void qb_log_target_free(struct qb_log_target *t);
+struct qb_log_target * qb_log_target_get(int32_t pos);
+
+int32_t qb_log_syslog_open(struct qb_log_target *t);
+int32_t qb_log_stderr_open(struct qb_log_target *t);
+int32_t qb_log_blackbox_open(struct qb_log_target *t);
+
+void qb_log_thread_stop(void);
+void qb_log_thread_log_post(struct qb_log_callsite *cs,
+			    time_t current_time,
+			    const char *buffer);
+void qb_log_thread_log_write(struct qb_log_callsite *cs,
+			    time_t current_time,
+			    const char *buffer);
+
+void qb_log_dcs_init(void);
+void qb_log_dcs_fini(void);
+struct qb_log_callsite *qb_log_dcs_get(int32_t *newly_created,
+				       const char *function,
+				       const char *filename,
+				       const char *format,
+				       uint8_t priority,
+				       uint32_t lineno,
+				       uint32_t tags);
+
+void qb_log_format_init(void);
+void qb_log_format_fini(void);
+const char * qb_log_priority2str(uint8_t priority);
+size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap);
+size_t qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf);
+
+void qb_log_target_format_static(int32_t target, const char * format, char *output_buffer);
+
+#endif /* _QB_LOG_INT_H_ */
+
diff --git a/lib/log_syslog.c b/lib/log_syslog.c
new file mode 100644
index 0000000..a6928be
--- /dev/null
+++ b/lib/log_syslog.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif /* HAVE_SYSLOG_H */
+#include "log_int.h"
+
+static void
+_syslog_logger(int32_t target,
+	       struct qb_log_callsite *cs, time_t timestamp, const char *msg)
+{
+	char output_buffer[QB_LOG_MAX_LEN];
+	struct qb_log_target *t = qb_log_target_get(target);
+	int32_t final_priority = cs->priority;
+
+	if (final_priority > LOG_INFO) {
+		/*
+		 * only bump the priority if it is greater than info.
+		 */
+		final_priority += t->priority_bump;
+	}
+	if (final_priority > LOG_DEBUG) {
+		return;
+	}
+
+	output_buffer[0] = '\0';
+	qb_log_target_format(target, cs, timestamp, msg, output_buffer);
+
+	if (final_priority < LOG_EMERG) {
+		final_priority = LOG_EMERG;
+	}
+	syslog(final_priority, "%s", output_buffer);
+}
+
+static void
+_syslog_close(int32_t target)
+{
+	closelog();
+}
+
+static void
+_syslog_reload(int32_t target)
+{
+	struct qb_log_target *t = qb_log_target_get(target);
+	closelog();
+	openlog(t->name, LOG_PID, t->facility);
+}
+
+int32_t
+qb_log_syslog_open(struct qb_log_target *t)
+{
+	t->logger = _syslog_logger;
+	t->reload = _syslog_reload;
+	t->close = _syslog_close;
+
+	openlog(t->name, LOG_PID, t->facility);
+	return 0;
+}
diff --git a/lib/log_thread.c b/lib/log_thread.c
new file mode 100644
index 0000000..56008f8
--- /dev/null
+++ b/lib/log_thread.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbutil.h>
+#include "log_int.h"
+
+static int wthread_active = QB_FALSE;
+
+static int wthread_should_exit = QB_FALSE;
+
+static qb_thread_lock_t *logt_wthread_lock = NULL;
+
+static QB_LIST_DECLARE(logt_print_finished_records);
+
+static int logt_memory_used = 0;
+
+static int logt_dropped_messages = 0;
+
+static sem_t logt_thread_start;
+
+static sem_t logt_print_finished;
+
+static int logt_sched_param_queued = QB_FALSE;
+
+static int logt_sched_policy;
+
+#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && defined(HAVE_SCHED_GET_PRIORITY_MAX)
+static struct sched_param logt_sched_param;
+#endif /* HAVE_PTHREAD_SETSCHEDPARAM && HAVE_SCHED_GET_PRIORITY_MAX */
+
+static pthread_t logt_thread_id = 0;
+
+static void *qb_logt_worker_thread(void *data) __attribute__ ((noreturn));
+static void *
+qb_logt_worker_thread(void *data)
+{
+	struct qb_log_record *rec;
+	int dropped = 0;
+	int res;
+
+	/*
+	 * Signal wthread_create that the initialization process may continue
+	 */
+	sem_post(&logt_thread_start);
+	for (;;) {
+retry_sem_wait:
+		res = sem_wait(&logt_print_finished);
+		if (res == -1 && errno == EINTR) {
+			goto retry_sem_wait;
+		} else if (res == -1) {
+			/*
+			 * This case shouldn't happen
+			 */
+			pthread_exit(NULL);
+		}
+
+		(void)qb_thread_lock(logt_wthread_lock);
+		if (wthread_should_exit) {
+			int value = -1;
+
+			(void)sem_getvalue(&logt_print_finished, &value);
+			if (value == 0) {
+				(void)qb_thread_unlock(logt_wthread_lock);
+				pthread_exit(NULL);
+			}
+		}
+
+		rec =
+		    qb_list_first_entry(&logt_print_finished_records,
+				  struct qb_log_record, list);
+		qb_list_del(&rec->list);
+		logt_memory_used = logt_memory_used - strlen(rec->buffer) -
+		    sizeof(struct qb_log_record) - 1;
+		dropped = logt_dropped_messages;
+		logt_dropped_messages = 0;
+		(void)qb_thread_unlock(logt_wthread_lock);
+		if (dropped) {
+			printf("%d messages lost\n", dropped);
+		}
+
+		qb_log_thread_log_write(rec->cs, rec->timestamp, rec->buffer);
+		free(rec->buffer);
+		free(rec);
+	}
+}
+
+int32_t
+qb_log_thread_priority_set(int32_t policy, int32_t priority)
+{
+	int res = 0;
+
+#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && defined(HAVE_SCHED_GET_PRIORITY_MAX)
+
+	logt_sched_policy = policy;
+
+	if (policy == SCHED_OTHER
+#ifdef SCHED_IDLE
+	    || policy == SCHED_IDLE
+#endif
+#if defined(SCHED_BATCH) && !defined(QB_DARWIN)
+	    || policy == SCHED_BATCH
+#endif
+	    ) {
+		logt_sched_param.sched_priority = 0;
+	} else {
+		logt_sched_param.sched_priority = priority;
+	}
+	if (wthread_active == QB_FALSE) {
+		logt_sched_param_queued = QB_TRUE;
+	} else {
+		res = pthread_setschedparam(logt_thread_id, policy,
+					    &logt_sched_param);
+		if (res != 0) {
+			res = -res;
+		}
+	}
+#endif
+	return res;
+}
+
+int32_t
+qb_log_thread_start(void)
+{
+	int res;
+
+	if (wthread_active) {
+		return 0;
+	}
+
+	wthread_active = QB_TRUE;
+	sem_init(&logt_thread_start, 0, 0);
+	sem_init(&logt_print_finished, 0, 0);
+	res = pthread_create(&logt_thread_id, NULL,
+			     qb_logt_worker_thread, NULL);
+	if (res != 0) {
+		wthread_active = QB_FALSE;
+		return -res;
+	}
+	sem_wait(&logt_thread_start);
+
+	if (logt_sched_param_queued) {
+		res = qb_log_thread_priority_set(logt_sched_policy,
+		                                 logt_sched_param.sched_priority);
+		if (res != 0) {
+			goto cleanup_pthread;
+		}
+		logt_sched_param_queued = QB_FALSE;
+	}
+	logt_wthread_lock = qb_thread_lock_create(QB_THREAD_LOCK_SHORT);
+	if (logt_wthread_lock == NULL) {
+		goto cleanup_pthread;
+	}
+
+	return 0;
+
+cleanup_pthread:
+	wthread_should_exit = QB_TRUE;
+	sem_post(&logt_print_finished);
+	pthread_join(logt_thread_id, NULL);
+	sem_destroy(&logt_print_finished);
+	sem_destroy(&logt_thread_start);
+
+	return res;
+}
+
+void
+qb_log_thread_log_post(struct qb_log_callsite *cs,
+		       time_t timestamp, const char *buffer)
+{
+	struct qb_log_record *rec;
+	size_t buf_size;
+	size_t total_size;
+
+	rec = malloc(sizeof(struct qb_log_record));
+	if (rec == NULL) {
+		return;
+	}
+
+	buf_size = strlen(buffer) + 1;
+	total_size = sizeof(struct qb_log_record) + buf_size;
+
+	rec->cs = cs;
+	rec->buffer = malloc(buf_size);
+	if (rec->buffer == NULL) {
+		goto free_record;
+	}
+	memcpy(rec->buffer, buffer, buf_size);
+
+	rec->timestamp = timestamp;
+
+	qb_list_init(&rec->list);
+	(void)qb_thread_lock(logt_wthread_lock);
+	logt_memory_used += total_size;
+	if (logt_memory_used > 512000) {
+		free(rec->buffer);
+		free(rec);
+		logt_memory_used = logt_memory_used - total_size;
+		logt_dropped_messages += 1;
+		(void)qb_thread_unlock(logt_wthread_lock);
+		return;
+
+	} else {
+		qb_list_add_tail(&rec->list, &logt_print_finished_records);
+	}
+	(void)qb_thread_unlock(logt_wthread_lock);
+
+	sem_post(&logt_print_finished);
+	return;
+
+free_record:
+	free(rec);
+}
+
+void
+qb_log_thread_stop(void)
+{
+	int res;
+	int value;
+	struct qb_log_record *rec;
+
+	if (wthread_active == QB_FALSE && logt_wthread_lock == NULL) {
+		return;
+	}
+	if (wthread_active == QB_FALSE) {
+		for (;;) {
+			res = sem_getvalue(&logt_print_finished, &value);
+			if (res != 0 || value == 0) {
+				break;
+			}
+			sem_wait(&logt_print_finished);
+
+			(void)qb_thread_lock(logt_wthread_lock);
+
+			rec = qb_list_first_entry(&logt_print_finished_records,
+					    struct qb_log_record, list);
+			qb_list_del(&rec->list);
+			logt_memory_used = logt_memory_used -
+					   strlen(rec->buffer) -
+					   sizeof(struct qb_log_record) - 1;
+			(void)qb_thread_unlock(logt_wthread_lock);
+
+			qb_log_thread_log_write(rec->cs, rec->timestamp,
+						rec->buffer);
+			free(rec->buffer);
+			free(rec);
+		}
+	} else {
+		wthread_should_exit = QB_TRUE;
+		sem_post(&logt_print_finished);
+		pthread_join(logt_thread_id, NULL);
+	}
+	(void)qb_thread_lock_destroy(logt_wthread_lock);
+	sem_destroy(&logt_print_finished);
+	sem_destroy(&logt_thread_start);
+}
diff --git a/lib/loop.c b/lib/loop.c
new file mode 100644
index 0000000..7f0176f
--- /dev/null
+++ b/lib/loop.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbloop.h>
+#include "loop_int.h"
+#include "util_int.h"
+
+static struct qb_loop *default_intance = NULL;
+
+static void
+qb_loop_run_level(struct qb_loop_level *level)
+{
+	struct qb_loop_item *job;
+	int32_t processed = 0;
+
+Ill_have_another:
+
+	if (!qb_list_empty(&level->job_head)) {
+		job = qb_list_first_entry(&level->job_head, struct qb_loop_item, list);
+		qb_list_del(&job->list);
+		qb_list_init(&job->list);
+		job->source->dispatch_and_take_back(job, level->priority);
+		level->todo--;
+		processed++;
+		if (level->l->stop_requested) {
+			return;
+		}
+		if (processed < level->to_process) {
+			goto Ill_have_another;
+		}
+	}
+}
+
+void
+qb_loop_level_item_add(struct qb_loop_level *level, struct qb_loop_item *job)
+{
+	qb_list_init(&job->list);
+	qb_list_add_tail(&job->list, &level->job_head);
+	level->todo++;
+}
+
+void
+qb_loop_level_item_del(struct qb_loop_level *level, struct qb_loop_item *job)
+{
+	/*
+	 * We may be deleted during dispatch... don't double-decrement todo.
+	 */
+	if (qb_list_empty(&job->list)) {
+		return;
+	}
+	qb_list_del(&job->list);
+	qb_list_init(&job->list);
+	level->todo--;
+}
+
+struct qb_loop *
+qb_loop_default_get(void)
+{
+	return default_intance;
+}
+
+struct qb_loop *
+qb_loop_create(void)
+{
+	struct qb_loop *l = malloc(sizeof(struct qb_loop));
+	int32_t p;
+
+	if (l == NULL) {
+		return NULL;
+	}
+	for (p = QB_LOOP_LOW; p <= QB_LOOP_HIGH; p++) {
+		l->level[p].priority = p;
+		l->level[p].to_process = 4;
+		l->level[p].todo = 0;
+		l->level[p].l = l;
+
+		qb_list_init(&l->level[p].job_head);
+		qb_list_init(&l->level[p].wait_head);
+	}
+
+	l->stop_requested = QB_FALSE;
+	l->timer_source = qb_loop_timer_create(l);
+	l->job_source = qb_loop_jobs_create(l);
+	l->fd_source = qb_loop_poll_create(l);
+	l->signal_source = qb_loop_signals_create(l);
+
+	if (default_intance == NULL) {
+		default_intance = l;
+	}
+	return l;
+}
+
+void
+qb_loop_destroy(struct qb_loop *l)
+{
+	qb_loop_timer_destroy(l);
+	qb_loop_jobs_destroy(l);
+	qb_loop_poll_destroy(l);
+	qb_loop_signals_destroy(l);
+
+	if (default_intance == l) {
+		default_intance = NULL;
+	}
+	free(l);
+}
+
+void
+qb_loop_stop(struct qb_loop *l)
+{
+	if (l == NULL) {
+		default_intance->stop_requested = QB_TRUE;
+	} else {
+		l->stop_requested = QB_TRUE;
+	}
+}
+
+void
+qb_loop_run(struct qb_loop *lp)
+{
+	int32_t p;
+	int32_t p_stop = QB_LOOP_LOW;
+	int32_t rc;
+	int32_t remaining_todo = 0;
+	int32_t job_todo;
+	int32_t timer_todo;
+	int32_t ms_timeout;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = default_intance;
+	}
+	l->stop_requested = QB_FALSE;
+
+	do {
+		if (p_stop == QB_LOOP_LOW) {
+			p_stop = QB_LOOP_HIGH;
+		} else {
+			p_stop--;
+		}
+
+		job_todo = 0;
+		if (l->job_source && l->job_source->poll) {
+			rc = l->job_source->poll(l->job_source, 0);
+			if (rc > 0) {
+				job_todo = rc;
+			} else if (rc == -1) {
+				errno = -rc;
+				qb_util_perror(LOG_WARNING, "job->poll");
+			}
+		}
+		timer_todo = 0;
+		if (l->timer_source && l->timer_source->poll) {
+			rc = l->timer_source->poll(l->timer_source, 0);
+			if (rc > 0) {
+				timer_todo = rc;
+			} else if (rc == -1) {
+				errno = -rc;
+				qb_util_perror(LOG_WARNING, "timer->poll");
+			}
+		}
+		if (remaining_todo > 0 || timer_todo > 0) {
+			/*
+			 * if there are remaining todos or timer todos then don't wait.
+			 */
+			ms_timeout = 0;
+		} else if (job_todo > 0) {
+			/*
+			 * if we only have jobs to do (not timers or old todos)
+			 * then set a non-zero timeout. Jobs can spin out of
+			 * control if someone keeps adding them.
+			 */
+			ms_timeout = 50;
+		} else {
+			if (l->timer_source) {
+				ms_timeout = qb_loop_timer_msec_duration_to_expire(l->timer_source);
+			} else {
+				ms_timeout = -1;
+			}
+		}
+		rc = l->fd_source->poll(l->fd_source, ms_timeout);
+		if (rc < 0) {
+			errno = -rc;
+			qb_util_perror(LOG_WARNING, "fd->poll");
+		}
+
+		remaining_todo = 0;
+		for (p = QB_LOOP_HIGH; p >= QB_LOOP_LOW; p--) {
+			if (p >= p_stop) {
+				qb_loop_run_level(&l->level[p]);
+				if (l->stop_requested) {
+					return;
+				}
+			}
+			remaining_todo += l->level[p].todo;
+		}
+	} while (!l->stop_requested);
+}
diff --git a/lib/loop_int.h b/lib/loop_int.h
new file mode 100644
index 0000000..672be21
--- /dev/null
+++ b/lib/loop_int.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_LOOP_INT_DEFINED
+#define QB_LOOP_INT_DEFINED
+
+#include <qb/qbloop.h>
+
+struct qb_loop;
+struct qb_loop_item;
+
+enum qb_loop_type {
+	QB_LOOP_FD,
+	QB_LOOP_JOB,
+	QB_LOOP_TIMER,
+	QB_LOOP_SIG,
+};
+
+struct qb_loop_item {
+	struct qb_list_head list;
+	struct qb_loop_source *source;
+	void *user_data;
+	enum qb_loop_type type;
+};
+
+struct qb_loop_level {
+	enum qb_loop_priority priority;
+	int32_t to_process;
+	int32_t todo;
+	struct qb_list_head wait_head;
+	struct qb_list_head job_head;
+	struct qb_loop *l;
+};
+
+struct qb_loop_source {
+	struct qb_loop *l;
+	void (*dispatch_and_take_back)(struct qb_loop_item *i,
+			 enum qb_loop_priority p);
+	int32_t (*poll)(struct qb_loop_source* s, int32_t ms_timeout);
+};
+
+struct qb_loop {
+	struct qb_loop_level level[3];
+	int32_t stop_requested;
+	struct qb_loop_source * timer_source;
+	struct qb_loop_source * job_source;
+	struct qb_loop_source * fd_source;
+	struct qb_loop_source * signal_source;
+};
+
+struct qb_loop *
+qb_loop_default_get(void);
+
+struct qb_loop_source *
+qb_loop_jobs_create(struct qb_loop *l);
+
+struct qb_loop_source*
+qb_loop_timer_create(struct qb_loop *l);
+
+struct qb_loop_source*
+qb_loop_poll_create(struct qb_loop *l);
+
+struct qb_loop_source *
+qb_loop_signals_create(struct qb_loop *l);
+
+void qb_loop_jobs_destroy(struct qb_loop *l);
+
+void qb_loop_timer_destroy(struct qb_loop *l);
+
+void qb_loop_poll_destroy(struct qb_loop *l);
+
+void qb_loop_signals_destroy(struct qb_loop *l);
+
+int32_t qb_loop_timer_msec_duration_to_expire(struct qb_loop_source *timer_source);
+
+void qb_loop_level_item_add(struct qb_loop_level *level,
+			    struct qb_loop_item *job);
+
+void qb_loop_level_item_del(struct qb_loop_level *level,
+			    struct qb_loop_item *job);
+
+enum qb_poll_entry_state {
+	QB_POLL_ENTRY_EMPTY,
+	QB_POLL_ENTRY_JOBLIST,
+	QB_POLL_ENTRY_DELETED,
+	QB_POLL_ENTRY_ACTIVE,
+};
+
+#endif /* QB_LOOP_INT_DEFINED */
+
diff --git a/lib/loop_job.c b/lib/loop_job.c
new file mode 100644
index 0000000..1d8cd49
--- /dev/null
+++ b/lib/loop_job.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbloop.h>
+#include "loop_int.h"
+#include "util_int.h"
+
+struct qb_loop_job {
+	struct qb_loop_item item;
+	qb_loop_job_dispatch_fn dispatch_fn;
+};
+
+static void
+job_dispatch(struct qb_loop_item *item, enum qb_loop_priority p)
+{
+	struct qb_loop_job *job = qb_list_entry(item, struct qb_loop_job, item);
+
+	job->dispatch_fn(job->item.user_data);
+	free(job);
+
+	/*
+	 * this is a one-shot so don't re-add
+	 */
+}
+
+static int32_t
+get_more_jobs(struct qb_loop_source *s, int32_t ms_timeout)
+{
+	int32_t p;
+	int32_t new_jobs = 0;
+	int32_t level_jobs = 0;
+
+	/*
+	 * this is simple, move jobs from wait_head to job_head
+	 */
+	for (p = QB_LOOP_LOW; p <= QB_LOOP_HIGH; p++) {
+		if (!qb_list_empty(&s->l->level[p].wait_head)) {
+			level_jobs = qb_list_length(&s->l->level[p].wait_head);
+			new_jobs += level_jobs;
+			qb_list_splice_tail(&s->l->level[p].wait_head,
+				            &s->l->level[p].job_head);
+			qb_list_init(&s->l->level[p].wait_head);
+			s->l->level[p].todo += level_jobs;
+		}
+	}
+	return new_jobs;
+}
+
+struct qb_loop_source *
+qb_loop_jobs_create(struct qb_loop *l)
+{
+	struct qb_loop_source *s = malloc(sizeof(struct qb_loop_source));
+	if (s == NULL) {
+		return NULL;
+	}
+	s->l = l;
+	s->dispatch_and_take_back = job_dispatch;
+	s->poll = get_more_jobs;
+
+	return s;
+}
+
+void
+qb_loop_jobs_destroy(struct qb_loop *l)
+{
+	free(l->job_source);
+}
+
+int32_t
+qb_loop_job_add(struct qb_loop *lp,
+		enum qb_loop_priority p,
+		void *data, qb_loop_job_dispatch_fn dispatch_fn)
+{
+	struct qb_loop_job *job;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	if (l == NULL || dispatch_fn == NULL) {
+		return -EINVAL;
+	}
+	if (p < QB_LOOP_LOW || p > QB_LOOP_HIGH) {
+		return -EINVAL;
+	}
+	job = malloc(sizeof(struct qb_loop_job));
+	if (job == NULL) {
+		return -ENOMEM;
+	}
+
+	job->dispatch_fn = dispatch_fn;
+	job->item.user_data = data;
+	job->item.source = l->job_source;
+	job->item.type = QB_LOOP_JOB;
+
+	qb_list_init(&job->item.list);
+	qb_list_add_tail(&job->item.list, &l->level[p].wait_head);
+
+	return 0;
+}
+
+int32_t
+qb_loop_job_del(struct qb_loop *lp,
+		enum qb_loop_priority p,
+		void *data, qb_loop_job_dispatch_fn dispatch_fn)
+{
+	struct qb_loop_job *job;
+	struct qb_loop_item *item;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	if (l == NULL || dispatch_fn == NULL) {
+		return -EINVAL;
+	}
+	if (p > QB_LOOP_HIGH) {
+		return -EINVAL;
+	}
+
+	qb_list_for_each_entry(item, &l->level[p].wait_head, list) {
+		job = (struct qb_loop_job *)item;
+		if (job->dispatch_fn == dispatch_fn &&
+		    job->item.user_data == data &&
+		    job->item.type == QB_LOOP_JOB) {
+			qb_list_del(&job->item.list);
+			free(job);
+			return 0;
+		}
+	}
+
+	qb_list_for_each_entry(item, &l->level[p].job_head, list) {
+
+		if (item->type != QB_LOOP_JOB) {
+			continue;
+		}
+		job = (struct qb_loop_job *)item;
+		if (job->dispatch_fn == dispatch_fn &&
+		    job->item.user_data == data) {
+			qb_loop_level_item_del(&l->level[p], item);
+			qb_util_log(LOG_DEBUG, "deleting job in JOBLIST");
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
diff --git a/lib/loop_poll.c b/lib/loop_poll.c
new file mode 100644
index 0000000..49c9650
--- /dev/null
+++ b/lib/loop_poll.c
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <signal.h>
+
+#if defined(__DARWIN_NSIG)
+#define QB_MAX_NUM_SIGNALS __DARWIN_NSIG
+#else
+  #if defined(NSIG)
+  #define QB_MAX_NUM_SIGNALS NSIG - 1
+  #else
+  #define QB_MAX_NUM_SIGNALS 31
+  #endif
+#endif
+
+#include "loop_poll_int.h"
+
+/*
+ * Define this to log slow (>10ms) jobs.
+ */
+#undef DEBUG_DISPATCH_TIME
+
+/* logs, std(in|out|err), pipe */
+#define POLL_FDS_USED_MISC 50
+
+#ifdef HAVE_EPOLL
+#define USE_EPOLL 1
+#else
+ #ifdef HAVE_KQUEUE
+ #define USE_KQUEUE 1
+ #else
+ #define USE_POLL 1
+ #endif /* HAVE_KQUEUE */
+#endif /* HAVE_EPOLL */
+
+static int32_t _qb_signal_add_to_jobs_(struct qb_loop *l,
+				       struct qb_poll_entry *pe);
+
+static void
+_poll_entry_check_generate_(struct qb_poll_entry *pe)
+{
+	int32_t i;
+
+	for (i = 0; i < 200; i++) {
+		pe->check = random();
+
+		if (pe->check != 0 && pe->check != 0xffffffff) {
+			break;
+		}
+	}
+}
+
+static void
+_poll_entry_mark_deleted_(struct qb_poll_entry *pe)
+{
+	pe->ufd.fd = -1;
+	pe->state = QB_POLL_ENTRY_DELETED;
+	pe->check = 0;
+}
+
+static void
+_poll_entry_empty_(struct qb_poll_entry *pe)
+{
+	memset(pe, 0, sizeof(struct qb_poll_entry));
+	pe->ufd.fd = -1;
+}
+
+static void
+_poll_dispatch_and_take_back_(struct qb_loop_item *item,
+			      enum qb_loop_priority p)
+{
+	struct qb_poll_entry *pe = (struct qb_poll_entry *)item;
+	int32_t res;
+#ifdef DEBUG_DISPATCH_TIME
+	uint64_t start;
+	uint64_t stop;
+	int32_t log_warn = QB_FALSE;
+
+	start = qb_util_nano_current_get();
+#endif /* DEBUG_DISPATCH_TIME */
+
+	assert(pe->state == QB_POLL_ENTRY_JOBLIST);
+	assert(pe->item.type == QB_LOOP_FD);
+
+	res = pe->poll_dispatch_fn(pe->ufd.fd,
+				   pe->ufd.revents,
+				   pe->item.user_data);
+	if (res < 0) {
+		_poll_entry_mark_deleted_(pe);
+	} else {
+		pe->state = QB_POLL_ENTRY_ACTIVE;
+		pe->ufd.revents = 0;
+	}
+#ifdef DEBUG_DISPATCH_TIME
+	if (pe->state == QB_POLL_ENTRY_ACTIVE) {
+		pe->runs++;
+		if ((pe->runs % 50) == 0) {
+			log_warn = QB_TRUE;
+		}
+		stop = qb_util_nano_current_get();
+		if ((stop - start) > (10 * QB_TIME_NS_IN_MSEC)) {
+			log_warn = QB_TRUE;
+		}
+
+		if (log_warn && pe->item.type == QB_LOOP_FD) {
+			qb_util_log(LOG_INFO,
+				    "[fd:%d] dispatch:%p runs:%d duration:%d ms",
+				    pe->ufd.fd, pe->poll_dispatch_fn,
+				    pe->runs,
+				    (int32_t) ((stop -
+						start) / QB_TIME_NS_IN_MSEC));
+		}
+	}
+#endif /* DEBUG_DISPATCH_TIME */
+}
+
+void
+qb_poll_fds_usage_check_(struct qb_poll_source *s)
+{
+	struct rlimit lim;
+	static int32_t socks_limit = 0;
+	int32_t send_event = QB_FALSE;
+	int32_t socks_used = 0;
+	int32_t socks_avail = 0;
+	struct qb_poll_entry *pe;
+	int32_t i;
+
+	if (socks_limit == 0) {
+		if (getrlimit(RLIMIT_NOFILE, &lim) == -1) {
+			qb_util_perror(LOG_WARNING, "getrlimit");
+			return;
+		}
+		socks_limit = lim.rlim_cur;
+		socks_limit -= POLL_FDS_USED_MISC;
+		if (socks_limit < 0) {
+			socks_limit = 0;
+		}
+	}
+
+	for (i = 0; i < s->poll_entry_count; i++) {
+		assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
+		if ((pe->state == QB_POLL_ENTRY_ACTIVE ||
+		     pe->state == QB_POLL_ENTRY_JOBLIST) && pe->ufd.fd != -1) {
+			socks_used++;
+		}
+		if (pe->state == QB_POLL_ENTRY_DELETED) {
+			_poll_entry_empty_(pe);
+		}
+	}
+
+	socks_avail = socks_limit - socks_used;
+	if (socks_avail < 0) {
+		socks_avail = 0;
+	}
+	send_event = QB_FALSE;
+	if (s->not_enough_fds) {
+		if (socks_avail > 2) {
+			s->not_enough_fds = QB_FALSE;
+			send_event = QB_TRUE;
+		}
+	} else {
+		if (socks_avail <= 1) {
+			s->not_enough_fds = QB_TRUE;
+			send_event = QB_TRUE;
+		}
+	}
+	if (send_event && s->low_fds_event_fn) {
+		s->low_fds_event_fn(s->not_enough_fds, socks_avail);
+	}
+}
+
+
+struct qb_loop_source *
+qb_loop_poll_create(struct qb_loop *l)
+{
+	struct qb_poll_source *s = malloc(sizeof(struct qb_poll_source));
+	if (s == NULL) {
+		return NULL;
+	}
+	s->s.l = l;
+	s->s.dispatch_and_take_back = _poll_dispatch_and_take_back_;
+
+	s->poll_entries = qb_array_create_2(16, sizeof(struct qb_poll_entry), 16);
+	s->poll_entry_count = 0;
+	s->low_fds_event_fn = NULL;
+	s->not_enough_fds = QB_FALSE;
+
+#ifdef USE_EPOLL
+	(void)qb_epoll_init(s);
+#endif
+#ifdef USE_KQUEUE
+	(void)qb_kqueue_init(s);
+#endif
+#ifdef USE_POLL
+	(void)qb_poll_init(s);
+#endif /* USE_POLL */
+
+	return (struct qb_loop_source *)s;
+}
+
+void
+qb_loop_poll_destroy(struct qb_loop *l)
+{
+	struct qb_poll_source *s = (struct qb_poll_source *)l->fd_source;
+	qb_array_free(s->poll_entries);
+
+	s->driver.fini(s);
+
+	free(s);
+}
+
+int32_t
+qb_loop_poll_low_fds_event_set(struct qb_loop *l,
+			       qb_loop_poll_low_fds_event_fn fn)
+{
+	struct qb_poll_source *s = (struct qb_poll_source *)l->fd_source;
+	s->low_fds_event_fn = fn;
+
+	return 0;
+}
+
+static int32_t
+_get_empty_array_position_(struct qb_poll_source *s)
+{
+	int32_t found = QB_FALSE;
+	uint32_t install_pos;
+	int32_t res = 0;
+	struct qb_poll_entry *pe;
+
+	for (install_pos = 0;
+	     install_pos < s->poll_entry_count; install_pos++) {
+		assert(qb_array_index
+		       (s->poll_entries, install_pos, (void **)&pe) == 0);
+		if (pe->state == QB_POLL_ENTRY_EMPTY) {
+			found = QB_TRUE;
+			break;
+		}
+	}
+
+	if (found == QB_FALSE) {
+#ifdef USE_POLL
+		struct pollfd *ufds;
+		int32_t new_size = (s->poll_entry_count + 1) * sizeof(struct pollfd);
+		ufds = realloc(s->ufds, new_size);
+		if (ufds == NULL) {
+			return -ENOMEM;
+		}
+		s->ufds = ufds;
+#endif /* USE_POLL */
+		/*
+		 * Grow pollfd list
+		 */
+		res = qb_array_grow(s->poll_entries, s->poll_entry_count + 1);
+		if (res != 0) {
+			return res;
+		}
+
+		s->poll_entry_count += 1;
+		install_pos = s->poll_entry_count - 1;
+	}
+	return install_pos;
+}
+
+static int32_t
+_poll_add_(struct qb_loop *l,
+	   enum qb_loop_priority p,
+	   int32_t fd, int32_t events, void *data, struct qb_poll_entry **pe_pt)
+{
+	struct qb_poll_entry *pe;
+	uint32_t install_pos;
+	int32_t res = 0;
+	struct qb_poll_source *s;
+
+	if (l == NULL) {
+		return -EINVAL;
+	}
+
+	s = (struct qb_poll_source *)l->fd_source;
+
+	install_pos = _get_empty_array_position_(s);
+
+	assert(qb_array_index(s->poll_entries, install_pos, (void **)&pe) == 0);
+	pe->state = QB_POLL_ENTRY_ACTIVE;
+	pe->install_pos = install_pos;
+	_poll_entry_check_generate_(pe);
+	pe->ufd.fd = fd;
+	pe->ufd.events = events;
+	pe->ufd.revents = 0;
+	pe->item.user_data = data;
+	pe->item.source = (struct qb_loop_source *)l->fd_source;
+	pe->p = p;
+	pe->runs = 0;
+	res = s->driver.add(s, pe, fd, events);
+	if (res == 0) {
+		*pe_pt = pe;
+		return 0;
+	} else {
+		pe->state = QB_POLL_ENTRY_EMPTY;
+		return res;
+	}
+}
+
+static int32_t
+_qb_poll_add_to_jobs_(struct qb_loop *l, struct qb_poll_entry *pe)
+{
+	assert(pe->item.type == QB_LOOP_FD);
+	qb_loop_level_item_add(&l->level[pe->p], &pe->item);
+	pe->state = QB_POLL_ENTRY_JOBLIST;
+	return 1;
+}
+
+int32_t
+qb_loop_poll_add(struct qb_loop * lp,
+		 enum qb_loop_priority p,
+		 int32_t fd,
+		 int32_t events,
+		 void *data, qb_loop_poll_dispatch_fn dispatch_fn)
+{
+	struct qb_poll_entry *pe = NULL;
+	int32_t size;
+	int32_t new_size;
+	int32_t res;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+
+	size = ((struct qb_poll_source *)l->fd_source)->poll_entry_count;
+	res = _poll_add_(l, p, fd, events, data, &pe);
+	if (res != 0) {
+		qb_util_perror(LOG_ERR,
+			       "couldn't add poll entryfor FD %d", fd);
+		return res;
+	}
+	new_size = ((struct qb_poll_source *)l->fd_source)->poll_entry_count;
+
+	pe->poll_dispatch_fn = dispatch_fn;
+	pe->item.type = QB_LOOP_FD;
+	pe->add_to_jobs = _qb_poll_add_to_jobs_;
+
+	if (new_size > size) {
+		qb_util_log(LOG_TRACE,
+			    "grown poll array to %d for FD %d", new_size, fd);
+	}
+
+	return res;
+}
+
+int32_t
+qb_loop_poll_mod(struct qb_loop * lp,
+		 enum qb_loop_priority p,
+		 int32_t fd,
+		 int32_t events,
+		 void *data, qb_loop_poll_dispatch_fn dispatch_fn)
+{
+	uint32_t i;
+	int32_t res = 0;
+	struct qb_poll_entry *pe;
+	struct qb_poll_source *s;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	s = (struct qb_poll_source *)l->fd_source;
+
+	/*
+	 * Find file descriptor to modify events and dispatch function
+	 */
+	for (i = 0; i < s->poll_entry_count; i++) {
+		assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
+		if (pe->ufd.fd != fd) {
+			continue;
+		}
+		if (pe->state == QB_POLL_ENTRY_DELETED || pe->check == 0) {
+			qb_util_log(LOG_ERR,
+				    "poll_mod : can't modify entry already deleted");
+			return -EBADF;
+		}
+		pe->poll_dispatch_fn = dispatch_fn;
+		pe->item.user_data = data;
+		pe->p = p;
+		if (pe->ufd.events != events) {
+			res = s->driver.mod(s, pe, fd, events);
+			pe->ufd.events = events;
+		}
+		return res;
+	}
+
+	return -EBADF;
+}
+
+int32_t
+qb_loop_poll_del(struct qb_loop * lp, int32_t fd)
+{
+	int32_t i;
+	int32_t res = 0;
+	struct qb_poll_entry *pe;
+	struct qb_poll_source *s;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	s = (struct qb_poll_source *)l->fd_source;
+	for (i = 0; i < s->poll_entry_count; i++) {
+		assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
+		if (pe->ufd.fd != fd || pe->item.type != QB_LOOP_FD) {
+			continue;
+		}
+		if (pe->state == QB_POLL_ENTRY_DELETED ||
+		    pe->state == QB_POLL_ENTRY_EMPTY) {
+			return 0;
+		}
+		if (pe->state == QB_POLL_ENTRY_JOBLIST) {
+			qb_loop_level_item_del(&l->level[pe->p], &pe->item);
+		}
+		res = s->driver.del(s, pe, fd, i);
+		_poll_entry_mark_deleted_(pe);
+		return res;
+	}
+
+	return -EBADF;
+}
+
+static int32_t pipe_fds[2] = { -1, -1 };
+
+struct qb_signal_source {
+	struct qb_loop_source s;
+	struct qb_list_head sig_head;
+	sigset_t signal_superset;
+};
+
+struct qb_loop_sig {
+	struct qb_loop_item item;
+	int32_t signal;
+	enum qb_loop_priority p;
+	qb_loop_signal_dispatch_fn dispatch_fn;
+	struct qb_loop_sig *cloned_from;
+};
+
+static void
+_handle_real_signal_(int signal_num, siginfo_t * si, void *context)
+{
+	int32_t sig = signal_num;
+	int32_t res = 0;
+
+	if (pipe_fds[1] > 0) {
+try_again:
+		res = write(pipe_fds[1], &sig, sizeof(int32_t));
+		if (res == -1 && errno == EAGAIN) {
+			goto try_again;
+		} else if (res != sizeof(int32_t)) {
+			qb_util_log(LOG_ERR,
+				    "failed to write signal to pipe [%d]", res);
+		}
+	}
+	qb_util_log(LOG_TRACE, "got real signal [%d] sent to pipe", sig);
+}
+
+static void
+_signal_dispatch_and_take_back_(struct qb_loop_item *item,
+				enum qb_loop_priority p)
+{
+	struct qb_loop_sig *sig = (struct qb_loop_sig *)item;
+	int32_t res;
+
+	res = sig->dispatch_fn(sig->signal, sig->item.user_data);
+	if (res != 0) {
+		(void)qb_loop_signal_del(sig->cloned_from->item.source->l,
+					 sig->cloned_from);
+	}
+	free(sig);
+}
+
+struct qb_loop_source *
+qb_loop_signals_create(struct qb_loop *l)
+{
+	int32_t res = 0;
+	struct qb_poll_entry *pe;
+	struct qb_signal_source *s = calloc(1, sizeof(struct qb_signal_source));
+
+	if (s == NULL) {
+		return NULL;
+	}
+	s->s.l = l;
+	s->s.dispatch_and_take_back = _signal_dispatch_and_take_back_;
+	s->s.poll = NULL;
+	qb_list_init(&s->sig_head);
+	sigemptyset(&s->signal_superset);
+
+	if (pipe_fds[0] < 0) {
+		res = pipe(pipe_fds);
+		if (res == -1) {
+			res = -errno;
+			qb_util_perror(LOG_ERR, "Can't light pipe");
+			goto error_exit;
+		}
+		(void)qb_sys_fd_nonblock_cloexec_set(pipe_fds[0]);
+		(void)qb_sys_fd_nonblock_cloexec_set(pipe_fds[1]);
+
+		res = _poll_add_(l, QB_LOOP_HIGH,
+				 pipe_fds[0], POLLIN, NULL, &pe);
+		if (res == 0) {
+			pe->poll_dispatch_fn = NULL;
+			pe->item.type = QB_LOOP_SIG;
+			pe->add_to_jobs = _qb_signal_add_to_jobs_;
+		} else {
+			qb_util_perror(LOG_ERR, "Can't smoke pipe");
+			goto error_exit;
+		}
+	}
+
+	return (struct qb_loop_source *)s;
+
+error_exit:
+	errno = -res;
+	free(s);
+	if (pipe_fds[0] >= 0) {
+		close(pipe_fds[0]);
+	}
+	if (pipe_fds[1] >= 0) {
+		close(pipe_fds[1]);
+	}
+	return NULL;
+}
+
+void
+qb_loop_signals_destroy(struct qb_loop *l)
+{
+	struct qb_signal_source *s =
+	    (struct qb_signal_source *)l->signal_source;
+	struct qb_list_head *list;
+	struct qb_list_head *n;
+	struct qb_loop_item *item;
+
+	close(pipe_fds[0]);
+	pipe_fds[0] = -1;
+	close(pipe_fds[1]);
+	pipe_fds[1] = -1;
+
+	qb_list_for_each_safe(list, n, &s->sig_head) {
+		item = qb_list_entry(list, struct qb_loop_item, list);
+		qb_list_del(&item->list);
+		free(item);
+	}
+
+	free(l->signal_source);
+}
+
+static int32_t
+_qb_signal_add_to_jobs_(struct qb_loop *l, struct qb_poll_entry *pe)
+{
+	struct qb_signal_source *s =
+	    (struct qb_signal_source *)l->signal_source;
+	struct qb_list_head *list;
+	struct qb_loop_sig *sig;
+	struct qb_loop_item *item;
+	struct qb_loop_sig *new_sig_job;
+	int32_t the_signal;
+	ssize_t res;
+	int32_t jobs_added = 0;
+
+	res = read(pipe_fds[0], &the_signal, sizeof(int32_t));
+	if (res != sizeof(int32_t)) {
+		qb_util_perror(LOG_WARNING, "failed to read pipe");
+		return 0;
+	}
+	pe->ufd.revents = 0;
+
+	qb_list_for_each(list, &s->sig_head) {
+		item = qb_list_entry(list, struct qb_loop_item, list);
+		sig = (struct qb_loop_sig *)item;
+		if (sig->signal == the_signal) {
+			new_sig_job = calloc(1, sizeof(struct qb_loop_sig));
+			if (new_sig_job == NULL) {
+				return jobs_added;
+			}
+			memcpy(new_sig_job, sig, sizeof(struct qb_loop_sig));
+
+			qb_util_log(LOG_TRACE,
+				    "adding signal [%d] to job queue %p",
+				    the_signal, sig);
+
+			new_sig_job->cloned_from = sig;
+			qb_loop_level_item_add(&l->level[sig->p],
+					       &new_sig_job->item);
+			jobs_added++;
+		}
+	}
+	return jobs_added;
+}
+
+static void
+_adjust_sigactions_(struct qb_signal_source *s)
+{
+	struct qb_loop_sig *sig;
+	struct qb_loop_item *item;
+	struct sigaction sa;
+	int32_t i;
+	int32_t needed;
+
+	sa.sa_flags = SA_SIGINFO;
+	sa.sa_sigaction = _handle_real_signal_;
+	sigemptyset(&s->signal_superset);
+	sigemptyset(&sa.sa_mask);
+
+	/* re-set to default */
+	for (i = 0; i < QB_MAX_NUM_SIGNALS; i++) {
+		needed = QB_FALSE;
+		qb_list_for_each_entry(item, &s->sig_head, list) {
+			sig = (struct qb_loop_sig *)item;
+			if (i == sig->signal) {
+				needed = QB_TRUE;
+				break;
+			}
+		}
+		if (needed) {
+			sigaddset(&s->signal_superset, i);
+			sigaction(i, &sa, NULL);
+		} else {
+			(void)signal(i, SIG_DFL);
+		}
+	}
+}
+
+int32_t
+qb_loop_signal_add(qb_loop_t * lp,
+		   enum qb_loop_priority p,
+		   int32_t the_sig,
+		   void *data,
+		   qb_loop_signal_dispatch_fn dispatch_fn,
+		   qb_loop_signal_handle * handle)
+{
+	struct qb_loop_sig *sig;
+	struct qb_signal_source *s;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	if (l == NULL || dispatch_fn == NULL) {
+		return -EINVAL;
+	}
+	if (p < QB_LOOP_LOW || p > QB_LOOP_HIGH) {
+		return -EINVAL;
+	}
+	s = (struct qb_signal_source *)l->signal_source;
+	sig = calloc(1, sizeof(struct qb_loop_sig));
+	if (sig == NULL) {
+		return -errno;
+	}
+
+	sig->dispatch_fn = dispatch_fn;
+	sig->p = p;
+	sig->signal = the_sig;
+	sig->item.user_data = data;
+	sig->item.source = l->signal_source;
+	sig->item.type = QB_LOOP_SIG;
+
+	qb_list_init(&sig->item.list);
+	qb_list_add_tail(&sig->item.list, &s->sig_head);
+
+	if (sigismember(&s->signal_superset, the_sig) != 1) {
+		_adjust_sigactions_(s);
+	}
+	if (handle) {
+		*handle = sig;
+	}
+
+	return 0;
+}
+
+int32_t
+qb_loop_signal_mod(qb_loop_t * lp,
+		   enum qb_loop_priority p,
+		   int32_t the_sig,
+		   void *data,
+		   qb_loop_signal_dispatch_fn dispatch_fn,
+		   qb_loop_signal_handle handle)
+{
+	struct qb_signal_source *s;
+	struct qb_loop_sig *sig = (struct qb_loop_sig *)handle;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	if (l == NULL || dispatch_fn == NULL || handle == NULL) {
+		return -EINVAL;
+	}
+	if (p < QB_LOOP_LOW || p > QB_LOOP_HIGH) {
+		return -EINVAL;
+	}
+	s = (struct qb_signal_source *)l->signal_source;
+
+	sig->item.user_data = data;
+	sig->item.type = QB_LOOP_SIG;
+	sig->dispatch_fn = dispatch_fn;
+	sig->p = p;
+
+	if (sig->signal != the_sig) {
+		sig->signal = the_sig;
+		_adjust_sigactions_(s);
+	}
+
+	return 0;
+}
+
+int32_t
+qb_loop_signal_del(qb_loop_t * lp, qb_loop_signal_handle handle)
+{
+	struct qb_signal_source *s;
+	struct qb_loop_sig *sig = (struct qb_loop_sig *)handle;
+	struct qb_loop_sig *sig_clone;
+	struct qb_loop *l = lp;
+	struct qb_loop_item *item;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	if (l == NULL || handle == NULL) {
+		return -EINVAL;
+	}
+	s = (struct qb_signal_source *)l->signal_source;
+
+	qb_list_for_each_entry(item, &l->level[sig->p].wait_head, list) {
+		if (item->type != QB_LOOP_SIG) {
+			continue;
+		}
+		sig_clone = (struct qb_loop_sig *)item;
+		if (sig_clone->cloned_from == sig) {
+			qb_util_log(LOG_TRACE, "deleting sig in WAITLIST");
+			qb_list_del(&sig_clone->item.list);
+			free(sig_clone);
+			break;
+		}
+	}
+
+	qb_list_for_each_entry(item, &l->level[sig->p].job_head, list) {
+		if (item->type != QB_LOOP_SIG) {
+			continue;
+		}
+		sig_clone = (struct qb_loop_sig *)item;
+		if (sig_clone->cloned_from == sig) {
+			qb_loop_level_item_del(&l->level[sig->p], item);
+			qb_util_log(LOG_TRACE, "deleting sig in JOBLIST");
+			break;
+		}
+	}
+
+	qb_list_del(&sig->item.list);
+	free(sig);
+	_adjust_sigactions_(s);
+	return 0;
+}
diff --git a/lib/loop_poll_epoll.c b/lib/loop_poll_epoll.c
new file mode 100644
index 0000000..7d1b9c7
--- /dev/null
+++ b/lib/loop_poll_epoll.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include "loop_poll_int.h"
+
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#ifndef epoll_create1
+int epoll_create1(int flags);
+#endif /* workaround a set of sparc and alpha broken headers */
+#endif /* HAVE_SYS_EPOLL_H */
+
+#define MAX_EVENTS 12
+
+static int32_t
+_poll_to_epoll_event_(int32_t event)
+{
+	int32_t out = 0;
+	if (event & POLLIN)
+		out |= EPOLLIN;
+	if (event & POLLOUT)
+		out |= EPOLLOUT;
+	if (event & POLLPRI)
+		out |= EPOLLPRI;
+	if (event & POLLERR)
+		out |= EPOLLERR;
+	if (event & POLLHUP)
+		out |= EPOLLHUP;
+	if (event & POLLNVAL)
+		out |= EPOLLERR;
+	return out;
+}
+
+static int32_t
+_epoll_to_poll_event_(int32_t event)
+{
+	int32_t out = 0;
+	if (event & EPOLLIN)
+		out |= POLLIN;
+	if (event & EPOLLOUT)
+		out |= POLLOUT;
+	if (event & EPOLLPRI)
+		out |= POLLPRI;
+	if (event & EPOLLERR)
+		out |= POLLERR;
+	if (event & EPOLLHUP)
+		out |= POLLHUP;
+	return out;
+}
+
+static void
+_fini(struct qb_poll_source *s)
+{
+	if (s->epollfd != -1) {
+		close(s->epollfd);
+		s->epollfd = -1;
+	}
+}
+
+static int32_t
+_add(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	struct epoll_event ev;
+	int32_t res = 0;
+
+	ev.events = _poll_to_epoll_event_(events);
+	ev.data.u64 = (((uint64_t) (pe->check)) << 32) | pe->install_pos;
+	if (epoll_ctl(s->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "epoll_ctl(add)");
+	}
+	return res;
+}
+
+
+static int32_t
+_mod(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	struct epoll_event ev;
+	int32_t res = 0;
+
+	ev.events = _poll_to_epoll_event_(events);
+	ev.data.u64 = (((uint64_t) (pe->check)) << 32) | pe->install_pos;
+	if (epoll_ctl(s->epollfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
+		res = -errno;
+		qb_util_perror(LOG_DEBUG, "epoll_ctl(mod)");
+	}
+	return res;
+}
+
+static int32_t
+_del(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t arr_index)
+{
+	int32_t res = 0;
+
+	if (epoll_ctl(s->epollfd, EPOLL_CTL_DEL, fd, NULL) == -1) {
+		res = -errno;
+		qb_util_perror(LOG_DEBUG, "epoll_ctl(del)");
+	}
+	return res;
+}
+
+static int32_t
+_poll_entry_from_handle_(struct qb_poll_source *s,
+			 uint64_t handle_in, struct qb_poll_entry **pe_pt)
+{
+	int32_t res = 0;
+	uint32_t check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	uint32_t handle = handle_in & 0xffffffff;
+	struct qb_poll_entry *pe;
+
+	res = qb_array_index(s->poll_entries, handle, (void **)&pe);
+	if (res != 0) {
+		return res;
+	}
+	if (pe->check != check) {
+		return -EINVAL;
+	}
+	*pe_pt = pe;
+	return 0;
+}
+
+static int32_t
+_poll_and_add_to_jobs_(struct qb_loop_source *src, int32_t ms_timeout)
+{
+	int32_t i;
+	int32_t res;
+	int32_t event_count;
+	int32_t new_jobs = 0;
+	struct qb_poll_entry *pe = NULL;
+	struct qb_poll_source *s = (struct qb_poll_source *)src;
+	struct epoll_event events[MAX_EVENTS];
+
+	qb_poll_fds_usage_check_(s);
+
+retry_poll:
+
+	event_count = epoll_wait(s->epollfd, events, MAX_EVENTS, ms_timeout);
+
+	if (errno == EINTR && event_count == -1) {
+		goto retry_poll;
+	} else if (event_count == -1) {
+		return -errno;
+	}
+
+	for (i = 0; i < event_count; i++) {
+		res = _poll_entry_from_handle_(s, events[i].data.u64, &pe);
+		if (res != 0) {
+			qb_util_log(LOG_WARNING,
+				    "can't find poll entry for new event.");
+			usleep(100000);
+			continue;
+		}
+		if (pe->ufd.fd == -1 || pe->state == QB_POLL_ENTRY_DELETED) {
+			qb_util_log(LOG_WARNING,
+				    "can't post new event to a deleted entry.");
+			/*
+			 * empty/deleted
+			 */
+			continue;
+		}
+
+		pe->ufd.revents |= _epoll_to_poll_event_(events[i].events);
+
+		if (pe->state != QB_POLL_ENTRY_JOBLIST) {
+			new_jobs += pe->add_to_jobs(src->l, pe);
+		}
+	}
+
+	return new_jobs;
+}
+
+int32_t
+qb_epoll_init(struct qb_poll_source *s)
+{
+	s->epollfd = epoll_create1(EPOLL_CLOEXEC);
+	if (s->epollfd < 0) {
+		return -errno;
+	}
+	s->driver.fini = _fini;
+	s->driver.add = _add;
+	s->driver.mod = _mod;
+	s->driver.del = _del;
+	s->s.poll = _poll_and_add_to_jobs_;
+	return 0;
+}
diff --git a/lib/loop_poll_int.h b/lib/loop_poll_int.h
new file mode 100644
index 0000000..f99f442
--- /dev/null
+++ b/lib/loop_poll_int.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef QB_LOOP_POLL_INT_DEFINED
+#define QB_LOOP_POLL_INT_DEFINED
+
+#include "os_base.h"
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif /* HAVE_SYS_POLL_H */
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbarray.h>
+#include <qb/qbutil.h>
+
+#include "loop_int.h"
+#include "util_int.h"
+
+struct qb_poll_entry;
+
+typedef int32_t(*qb_poll_add_to_jobs_fn) (struct qb_loop * l,
+					  struct qb_poll_entry * pe);
+
+struct qb_poll_entry {
+	struct qb_loop_item item;
+	qb_loop_poll_dispatch_fn poll_dispatch_fn;
+	enum qb_loop_priority p;
+	uint32_t install_pos;
+	struct pollfd ufd;
+	qb_poll_add_to_jobs_fn add_to_jobs;
+	uint32_t runs;
+	enum qb_poll_entry_state state;
+	uint32_t check;
+};
+
+struct qb_poll_source;
+
+struct qb_loop_driver {
+	int32_t (*poll)(struct qb_loop_source* s, int32_t ms_timeout);
+	void (*fini)(struct qb_poll_source *s);
+	int32_t (*add)(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events);
+	int32_t (*mod)(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events);
+	int32_t (*del)(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t arr_index);
+};
+
+struct qb_poll_source {
+	struct qb_loop_source s;
+	int32_t poll_entry_count;
+	qb_array_t *poll_entries;
+	qb_loop_poll_low_fds_event_fn low_fds_event_fn;
+	int32_t not_enough_fds;
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+	int32_t epollfd;
+#else
+	struct pollfd *ufds;
+#endif /* HAVE_EPOLL */
+	struct qb_loop_driver driver;
+};
+
+void
+qb_poll_fds_usage_check_(struct qb_poll_source *s);
+
+int32_t
+qb_epoll_init(struct qb_poll_source *s);
+
+int32_t
+qb_poll_init(struct qb_poll_source *s);
+
+int32_t
+qb_kqueue_init(struct qb_poll_source *s);
+
+#endif /* QB_LOOP_POLL_INT_DEFINED */
diff --git a/lib/loop_poll_kqueue.c b/lib/loop_poll_kqueue.c
new file mode 100644
index 0000000..871e4b0
--- /dev/null
+++ b/lib/loop_poll_kqueue.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include "loop_poll_int.h"
+
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#endif /* HAVE_SYS_EVENT_H */
+
+#define MAX_EVENTS 12
+
+static int32_t
+_poll_to_filter_(int32_t event)
+{
+	int32_t out = 0;
+	if (event & POLLIN)
+		out |= EVFILT_READ;
+	if (event & POLLOUT)
+		out |= EVFILT_WRITE;
+	return out;
+}
+
+static void
+_fini(struct qb_poll_source *s)
+{
+	if (s->epollfd != -1) {
+		close(s->epollfd);
+		s->epollfd = -1;
+	}
+}
+
+static int32_t
+_add(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	int32_t res = 0;
+	struct kevent ke;
+	short filters = _poll_to_filter_(events);
+
+	EV_SET(&ke, fd, filters, EV_ADD | EV_ENABLE, 0, 0, (intptr_t)pe);
+
+	res = kevent(s->epollfd, &ke, 1, NULL, 0, NULL);
+	if (res == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "kevent(add)");
+	}
+
+	return res;
+}
+
+static int32_t
+_mod(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	int32_t res = 0;
+	struct kevent ke[2];
+	short new_filters = _poll_to_filter_(events);
+	short old_filters = _poll_to_filter_(pe->ufd.events);
+
+	EV_SET(&ke[0], fd, old_filters, EV_DELETE, 0, 0, (intptr_t)pe);
+	EV_SET(&ke[1], fd, new_filters, EV_ADD | EV_ENABLE, 0, 0, (intptr_t)pe);
+
+	res = kevent(s->epollfd, ke, 2, NULL, 0, NULL);
+	if (res == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "kevent(mod)");
+	}
+	return res;
+}
+
+static int32_t
+_del(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	int32_t res = 0;
+	struct kevent ke;
+	short filters = _poll_to_filter_(events);
+
+	EV_SET(&ke, fd, filters, EV_DELETE, 0, 0, (intptr_t)pe);
+
+	res = kevent(s->epollfd, &ke, 1, NULL, 0, NULL);
+	if (res == -1 && errno == ENOENT) {
+		/*
+		 * Not a problem already cleaned up.
+		 */
+		res = 0;
+	} else if (res == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "kevent(del)");
+	}
+	return res;
+}
+
+static int32_t
+_poll_and_add_to_jobs_(struct qb_loop_source *src, int32_t ms_timeout)
+{
+	int32_t i;
+	int32_t event_count;
+	int32_t new_jobs = 0;
+	int32_t revents = 0;
+	struct qb_poll_entry *pe = NULL;
+	struct qb_poll_source *s = (struct qb_poll_source *)src;
+	struct kevent events[MAX_EVENTS];
+	struct timespec timeout = { 0, 0 };
+	struct timespec *timeout_pt = &timeout;
+
+	if (ms_timeout > 0) {
+		qb_timespec_add_ms(&timeout, ms_timeout);
+	} else if (ms_timeout < 0) {
+		timeout_pt = NULL;
+	}
+	qb_poll_fds_usage_check_(s);
+
+retry_poll:
+
+	event_count = kevent(s->epollfd, NULL, 0, events, MAX_EVENTS, timeout_pt);
+	if (errno == EINTR && event_count == -1) {
+		goto retry_poll;
+	} else if (event_count == -1) {
+		qb_util_perror(LOG_ERR, "kevent(poll)");
+		return -errno;
+	}
+
+	for (i = 0; i < event_count; i++) {
+		revents = 0;
+		pe = (struct qb_poll_entry *)events[i].udata;
+#if 0
+		if (events[i].flags) {
+			qb_util_log(LOG_TRACE,
+				    "got flags %d on fd %d.", events[i].flags,
+				    pe->ufd.fd);
+		}
+#endif
+		if (events[i].flags & EV_ERROR) {
+			qb_util_log(LOG_WARNING,
+				    "got EV_ERROR on fd %d.", pe->ufd.fd);
+			revents |= POLLERR;
+		}
+		if (events[i].flags & EV_EOF) {
+			qb_util_log(LOG_INFO,
+				    "got EV_EOF on fd %d.", pe->ufd.fd);
+			revents |= POLLHUP;
+		}
+		if (events[i].filter == EVFILT_READ) {
+			revents |= POLLIN;
+		}
+		if (events[i].filter == EVFILT_WRITE) {
+			revents |= POLLOUT;
+		}
+		if (pe->ufd.fd == -1 || pe->state == QB_POLL_ENTRY_DELETED) {
+			qb_util_log(LOG_WARNING,
+				    "can't post new event to a deleted entry.");
+			/*
+			 * empty/deleted
+			 */
+			EV_SET(&events[i], events[i].ident, events[i].filter,
+			       EV_DELETE, 0, 0, (intptr_t)pe);
+			(void)kevent(s->epollfd, &events[i], 1, NULL, 0, NULL);
+			continue;
+		}
+		if (pe->ufd.fd != events[i].ident) {
+			qb_util_log(LOG_WARNING,
+				    "can't find poll entry for new event.");
+			continue;
+		}
+		if (revents == pe->ufd.revents ||
+		    pe->state == QB_POLL_ENTRY_JOBLIST) {
+			/*
+			 * entry already in the job queue.
+			 */
+			continue;
+		}
+		pe->ufd.revents = revents;
+
+		new_jobs += pe->add_to_jobs(src->l, pe);
+	}
+
+	return new_jobs;
+}
+
+int32_t
+qb_kqueue_init(struct qb_poll_source *s)
+{
+	s->epollfd = kqueue();
+
+	if (s->epollfd < 0) {
+		qb_util_perror(LOG_ERR, "kqueue()");
+		return -errno;
+	}
+	s->driver.fini = _fini;
+	s->driver.add = _add;
+	s->driver.mod = _mod;
+	s->driver.del = _del;
+	s->s.poll = _poll_and_add_to_jobs_;
+	return 0;
+}
diff --git a/lib/loop_poll_poll.c b/lib/loop_poll_poll.c
new file mode 100644
index 0000000..ecc7418
--- /dev/null
+++ b/lib/loop_poll_poll.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include "loop_poll_int.h"
+
+
+static void
+_fini(struct qb_poll_source *s)
+{
+}
+
+static int32_t
+_add(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	return 0;
+}
+
+static int32_t
+_mod(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t events)
+{
+	return 0;
+}
+
+static int32_t
+_del(struct qb_poll_source *s, struct qb_poll_entry *pe, int32_t fd, int32_t i)
+{
+	s->ufds[i].fd = -1;
+	s->ufds[i].events = 0;
+	s->ufds[i].revents = 0;
+	return 0;
+}
+
+static int32_t
+_poll_and_add_to_jobs_(struct qb_loop_source *src, int32_t ms_timeout)
+{
+	int32_t i;
+	int32_t res;
+	int32_t new_jobs = 0;
+	struct qb_poll_entry *pe;
+	struct qb_poll_source *s = (struct qb_poll_source *)src;
+
+	qb_poll_fds_usage_check_(s);
+
+	for (i = 0; i < s->poll_entry_count; i++) {
+		assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
+		memcpy(&s->ufds[i], &pe->ufd, sizeof(struct pollfd));
+	}
+
+retry_poll:
+	res = poll(s->ufds, s->poll_entry_count, ms_timeout);
+	if (errno == EINTR && res == -1) {
+		goto retry_poll;
+	} else if (res == -1) {
+		return -errno;
+	}
+
+	for (i = 0; i < s->poll_entry_count; i++) {
+		if (s->ufds[i].fd == -1 || s->ufds[i].revents == 0) {
+			/*
+			 * empty entry
+			 */
+			continue;
+		}
+		assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
+		if (pe->state != QB_POLL_ENTRY_ACTIVE ||
+		    s->ufds[i].revents == pe->ufd.revents) {
+			/*
+			 * Wrong state to accept an event.
+			 */
+			continue;
+		}
+		pe->ufd.revents = s->ufds[i].revents;
+		new_jobs += pe->add_to_jobs(src->l, pe);
+	}
+
+	return new_jobs;
+}
+
+int32_t
+qb_poll_init(struct qb_poll_source *s)
+{
+	s->ufds = 0;
+	s->driver.fini = _fini;
+	s->driver.add = _add;
+	s->driver.mod = _mod;
+	s->driver.del = _del;
+	s->s.poll = _poll_and_add_to_jobs_;
+	return 0;
+}
+
diff --git a/lib/loop_timerlist.c b/lib/loop_timerlist.c
new file mode 100644
index 0000000..f4d32fb
--- /dev/null
+++ b/lib/loop_timerlist.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <pthread.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbarray.h>
+#include <qb/qbloop.h>
+#include "loop_int.h"
+#include "util_int.h"
+#include "tlist.h"
+
+struct qb_loop_timer {
+	struct qb_loop_item item;
+	qb_loop_timer_dispatch_fn dispatch_fn;
+	enum qb_loop_priority p;
+	timer_handle timerlist_handle;
+	enum qb_poll_entry_state state;
+	uint32_t check;
+	uint32_t install_pos;
+};
+
+struct qb_timer_source {
+	struct qb_loop_source s;
+	struct timerlist timerlist;
+	qb_array_t *timers;
+	size_t timer_entry_count;
+};
+
+static void
+timer_dispatch(struct qb_loop_item *item, enum qb_loop_priority p)
+{
+	struct qb_loop_timer *timer = (struct qb_loop_timer *)item;
+
+	assert(timer->state == QB_POLL_ENTRY_JOBLIST);
+	timer->check = 0;
+	timer->dispatch_fn(timer->item.user_data);
+	timer->state = QB_POLL_ENTRY_EMPTY;
+}
+
+static int32_t expired_timers;
+static void
+make_job_from_tmo(void *data)
+{
+	struct qb_loop_timer *t = (struct qb_loop_timer *)data;
+	struct qb_loop *l = t->item.source->l;
+
+	assert(t->state == QB_POLL_ENTRY_ACTIVE);
+	qb_loop_level_item_add(&l->level[t->p], &t->item);
+	t->state = QB_POLL_ENTRY_JOBLIST;
+	expired_timers++;
+}
+
+static int32_t
+expire_the_timers(struct qb_loop_source *s, int32_t ms_timeout)
+{
+	struct qb_timer_source *ts = (struct qb_timer_source *)s;
+	expired_timers = 0;
+	timerlist_expire(&ts->timerlist);
+	return expired_timers;
+}
+
+int32_t
+qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source)
+{
+	struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source;
+	uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist);
+	if (left != -1 && left > 0xFFFFFFFF) {
+		left = 0xFFFFFFFE;
+	}
+	return left;
+}
+
+struct qb_loop_source *
+qb_loop_timer_create(struct qb_loop *l)
+{
+	struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source));
+	if (my_src == NULL) {
+		return NULL;
+	}
+	my_src->s.l = l;
+	my_src->s.dispatch_and_take_back = timer_dispatch;
+	my_src->s.poll = expire_the_timers;
+
+	timerlist_init(&my_src->timerlist);
+	my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16);
+	my_src->timer_entry_count = 0;
+
+	return (struct qb_loop_source *)my_src;
+}
+
+void
+qb_loop_timer_destroy(struct qb_loop *l)
+{
+	struct qb_timer_source *my_src =
+	    (struct qb_timer_source *)l->timer_source;
+	qb_array_free(my_src->timers);
+	free(l->timer_source);
+}
+
+static int32_t
+_timer_from_handle_(struct qb_timer_source *s,
+		    qb_loop_timer_handle handle_in,
+		    struct qb_loop_timer **timer_pt)
+{
+	int32_t rc;
+	uint32_t check;
+	uint32_t install_pos;
+	struct qb_loop_timer *timer;
+
+	if (handle_in == 0) {
+		return -EINVAL;
+	}
+
+	check = ((uint32_t) (((uint64_t) handle_in) >> 32));
+	install_pos = handle_in & 0xffffffff;
+
+	rc = qb_array_index(s->timers, install_pos, (void **)&timer);
+	if (rc != 0) {
+		return rc;
+	}
+	if (timer->check != check) {
+		return -EINVAL;
+	}
+	*timer_pt = timer;
+	return 0;
+}
+
+static int32_t
+_get_empty_array_position_(struct qb_timer_source *s)
+{
+	int32_t install_pos;
+	int32_t res = 0;
+	struct qb_loop_timer *timer;
+
+	for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) {
+		assert(qb_array_index(s->timers, install_pos, (void **)&timer)
+		       == 0);
+		if (timer->state == QB_POLL_ENTRY_EMPTY) {
+			return install_pos;
+		}
+	}
+
+	res = qb_array_grow(s->timers, s->timer_entry_count + 1);
+	if (res != 0) {
+		return res;
+	}
+
+	s->timer_entry_count++;
+	install_pos = s->timer_entry_count - 1;
+	return install_pos;
+}
+
+int32_t
+qb_loop_timer_add(struct qb_loop * lp,
+		  enum qb_loop_priority p,
+		  uint64_t nsec_duration,
+		  void *data,
+		  qb_loop_timer_dispatch_fn timer_fn,
+		  qb_loop_timer_handle * timer_handle_out)
+{
+	struct qb_loop_timer *t;
+	struct qb_timer_source *my_src;
+	int32_t i;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+
+	if (l == NULL || timer_fn == NULL) {
+		return -EINVAL;
+	}
+	my_src = (struct qb_timer_source *)l->timer_source;
+
+	i = _get_empty_array_position_(my_src);
+	assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0);
+	t->state = QB_POLL_ENTRY_ACTIVE;
+	t->install_pos = i;
+	t->item.user_data = data;
+	t->item.source = (struct qb_loop_source *)my_src;
+	t->dispatch_fn = timer_fn;
+	t->p = p;
+	qb_list_init(&t->item.list);
+
+	for (i = 0; i < 200; i++) {
+		t->check = random();
+
+		if (t->check != 0 && t->check != 0xffffffff) {
+			break;
+		}
+	}
+
+	if (timer_handle_out) {
+		*timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos;
+	}
+	return timerlist_add_duration(&my_src->timerlist,
+				      make_job_from_tmo, t,
+				      nsec_duration, &t->timerlist_handle);
+}
+
+int32_t
+qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th)
+{
+	struct qb_timer_source *s;
+	struct qb_loop_timer *t;
+	int32_t res;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	s = (struct qb_timer_source *)l->timer_source;
+
+	res = _timer_from_handle_(s, th, &t);
+	if (res != 0) {
+		return res;
+	}
+
+	if (t->state == QB_POLL_ENTRY_DELETED) {
+		qb_util_log(LOG_WARNING, "timer already deleted");
+		return 0;
+	}
+	if (t->state != QB_POLL_ENTRY_ACTIVE &&
+	    t->state != QB_POLL_ENTRY_JOBLIST) {
+		return -EINVAL;
+	}
+	if (t->state == QB_POLL_ENTRY_JOBLIST) {
+		qb_loop_level_item_del(&l->level[t->p], &t->item);
+	}
+
+	if (t->timerlist_handle) {
+		timerlist_del(&s->timerlist, t->timerlist_handle);
+	}
+	t->state = QB_POLL_ENTRY_EMPTY;
+	return 0;
+}
+
+uint64_t
+qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th)
+{
+	struct qb_timer_source *s;
+	struct qb_loop_timer *t;
+	int32_t res;
+	struct qb_loop *l = lp;
+
+	if (l == NULL) {
+		l = qb_loop_default_get();
+	}
+	s = (struct qb_timer_source *)l->timer_source;
+
+	res = _timer_from_handle_(s, th, &t);
+	if (res != 0) {
+		return 0;
+	}
+
+	if (t->state != QB_POLL_ENTRY_ACTIVE) {
+		return 0;
+	}
+
+	return timerlist_expire_time(&s->timerlist, t->timerlist_handle);
+}
+
+int32_t
+qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th)
+{
+	return (qb_loop_timer_expire_time_get(l, th) > 0);
+}
diff --git a/lib/map.c b/lib/map.c
new file mode 100644
index 0000000..d3aa8f5
--- /dev/null
+++ b/lib/map.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <qb/qbmap.h>
+#include "map_int.h"
+
+void
+qb_map_put(struct qb_map *map, const char *key, const void *value)
+{
+	map->put(map, key, value);
+}
+
+void *
+qb_map_get(struct qb_map *map, const char *key)
+{
+	return map->get(map, key);
+}
+
+int32_t
+qb_map_rm(struct qb_map * map, const char *key)
+{
+	return map->rm(map, key);
+}
+
+size_t
+qb_map_count_get(struct qb_map * map)
+{
+	return map->count_get(map);
+}
+
+void
+qb_map_foreach(struct qb_map *map, qb_map_transverse_fn func, void *user_data)
+{
+	const char *key;
+	void *value;
+	qb_map_iter_t *i = qb_map_iter_create(map);
+
+	for (key = qb_map_iter_next(i, &value);
+	     key; key = qb_map_iter_next(i, &value)) {
+		if (func(key, value, user_data)) {
+			goto clean_up;
+		}
+	}
+clean_up:
+	qb_map_iter_free(i);
+}
+
+qb_map_iter_t *
+qb_map_iter_create(struct qb_map *map)
+{
+	return map->iter_create(map, NULL);
+}
+
+qb_map_iter_t *
+qb_map_pref_iter_create(qb_map_t * map, const char *prefix)
+{
+	return map->iter_create(map, prefix);
+}
+
+const char *
+qb_map_iter_next(struct qb_map_iter *i, void **value)
+{
+	return i->m->iter_next(i, value);
+}
+
+void
+qb_map_iter_free(qb_map_iter_t * i)
+{
+	i->m->iter_free(i);
+}
+
+int32_t
+qb_map_notify_add(qb_map_t * m, const char *key, qb_map_notify_fn fn,
+		  int32_t events, void *user_data)
+{
+	if (key != NULL && events & QB_MAP_NOTIFY_FREE) {
+		return -EINVAL;
+	}
+	if (m->notify_add) {
+		return m->notify_add(m, key, fn, events, user_data);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int32_t
+qb_map_notify_del(qb_map_t * m, const char *key, qb_map_notify_fn fn,
+		  int32_t events)
+{
+	if (m->notify_del) {
+		return m->notify_del(m, key, fn, events, QB_FALSE, NULL);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int32_t
+qb_map_notify_del_2(qb_map_t * m, const char *key, qb_map_notify_fn fn,
+		    int32_t events, void *user_data)
+{
+	if (m->notify_del) {
+		return m->notify_del(m, key, fn, events, QB_TRUE, user_data);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+void
+qb_map_destroy(struct qb_map *map)
+{
+	map->destroy(map);
+}
diff --git a/lib/map_int.h b/lib/map_int.h
new file mode 100644
index 0000000..d6aa7fd
--- /dev/null
+++ b/lib/map_int.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _QB_MAP_INT_H_
+#define _QB_MAP_INT_H_
+
+#include <qb/qblist.h>
+
+struct qb_map;
+
+typedef void (*qb_map_put_func)(struct qb_map *map, const char* key,
+				const void* value);
+typedef void* (*qb_map_get_func)(struct qb_map *map, const char* key);
+typedef int32_t (*qb_map_rm_func)(struct qb_map *map, const char* key);
+typedef size_t (*qb_map_count_get_func)(struct qb_map *map);
+typedef void (*qb_map_destroy_func)(struct qb_map *map);
+typedef qb_map_iter_t* (*qb_map_iter_create_func)(struct qb_map *map,
+						  const char* prefix);
+typedef const char* (*qb_map_iter_next_func)(qb_map_iter_t* i, void** value);
+typedef void (*qb_map_iter_free_func)(qb_map_iter_t* i);
+
+typedef int32_t (*qb_map_notify_add_func)(qb_map_t* m, const char* key,
+					  qb_map_notify_fn fn, int32_t events,
+					  void *user_data);
+
+typedef int32_t (*qb_map_notify_del_func)(qb_map_t * m, const char *key,
+					  qb_map_notify_fn fn,
+					  int32_t events,
+					  int32_t cmp_userdata,
+					  void *user_data);
+
+struct qb_map {
+	qb_map_put_func put;
+	qb_map_get_func get;
+	qb_map_rm_func rm;
+	qb_map_count_get_func count_get;
+	qb_map_destroy_func destroy;
+	qb_map_iter_create_func iter_create;
+	qb_map_iter_next_func iter_next;
+	qb_map_iter_free_func iter_free;
+	qb_map_notify_add_func notify_add;
+	qb_map_notify_del_func notify_del;
+};
+
+struct qb_map_iter {
+	struct qb_map *m;
+};
+
+struct qb_map_notifier {
+	struct qb_list_head list;
+	qb_map_notify_fn callback;
+	int32_t events;
+	void *user_data;
+	int32_t refcount;
+};
+
+
+#endif /* _QB_MAP_INT_H_ */
diff --git a/lib/ringbuffer.c b/lib/ringbuffer.c
new file mode 100644
index 0000000..f6b1971
--- /dev/null
+++ b/lib/ringbuffer.c
@@ -0,0 +1,960 @@
+/*
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "ringbuffer_int.h"
+#include <qb/qbdefs.h>
+#include "atomic_int.h"
+
+#define QB_RB_FILE_HEADER_VERSION 1
+
+/*
+ * #define CRAZY_DEBUG_PRINTFS 1
+ */
+#ifdef CRAZY_DEBUG_PRINTFS
+#define DEBUG_PRINTF(format, args...)	\
+do {				\
+	printf(format, ##args);	\
+} while(0)
+#else
+#define DEBUG_PRINTF(format, args...)
+#endif /* CRAZY_DEBUG_PRINTFS */
+
+/*
+ * move the write pointer to the next 128 byte boundary
+ * write_pt goes in 4 bytes (sizeof(uint32_t))
+ * #define USE_CACHE_LINE_ALIGNMENT 1
+ */
+#ifdef USE_CACHE_LINE_ALIGNMENT
+#define QB_CACHE_LINE_SIZE 128
+#define QB_CACHE_LINE_WORDS (QB_CACHE_LINE_SIZE/sizeof(uint32_t))
+#define idx_cache_line_step(idx)	\
+do {					\
+	if (idx % QB_CACHE_LINE_WORDS) {			\
+		idx += (QB_CACHE_LINE_WORDS - (idx % QB_CACHE_LINE_WORDS));	\
+	}				\
+	if (idx > (rb->shared_hdr->word_size - 1)) {		\
+		idx = ((idx) % (rb->shared_hdr->word_size));	\
+	}						\
+} while (0)
+#else
+#define QB_CACHE_LINE_SIZE 0
+#define QB_CACHE_LINE_WORDS 0
+#define idx_cache_line_step(idx)			\
+do {							\
+	if (idx > (rb->shared_hdr->word_size - 1)) {		\
+		idx = ((idx) % (rb->shared_hdr->word_size));	\
+	}						\
+} while (0)
+#endif
+
+
+/* the chunk header is two words
+ * 1) the chunk data size
+ * 2) the magic number
+ */
+#define QB_RB_CHUNK_HEADER_WORDS 2
+#define QB_RB_CHUNK_HEADER_SIZE (sizeof(uint32_t) * QB_RB_CHUNK_HEADER_WORDS)
+/*
+ * margin is the gap we leave when checking to see if we have enough
+ * space for a new chunk.
+ * So:
+ * qb_rb_space_free() >= QB_RB_CHUNK_MARGIN + new data chunk
+ * The extra word size is to allow for non word sized data chunks.
+ * QB_CACHE_LINE_WORDS is to make sure we have space to align the
+ * chunk.
+ */
+#define QB_RB_WORD_ALIGN 1
+#define QB_RB_CHUNK_MARGIN (sizeof(uint32_t) * (QB_RB_CHUNK_HEADER_WORDS +\
+						QB_RB_WORD_ALIGN +\
+						QB_CACHE_LINE_WORDS))
+#define QB_RB_CHUNK_MAGIC		0xA1A1A1A1
+#define QB_RB_CHUNK_MAGIC_DEAD		0xD0D0D0D0
+#define QB_RB_CHUNK_MAGIC_ALLOC		0xA110CED0
+#define QB_RB_CHUNK_SIZE_GET(rb, pointer) rb->shared_data[pointer]
+#define QB_RB_CHUNK_MAGIC_GET(rb, pointer) \
+	qb_atomic_int_get_ex((int32_t*)&rb->shared_data[(pointer + 1) % rb->shared_hdr->word_size], \
+                             QB_ATOMIC_ACQUIRE)
+#define QB_RB_CHUNK_MAGIC_SET(rb, pointer, new_val) \
+	qb_atomic_int_set_ex((int32_t*)&rb->shared_data[(pointer + 1) % rb->shared_hdr->word_size], \
+			     new_val, QB_ATOMIC_RELEASE)
+#define QB_RB_CHUNK_DATA_GET(rb, pointer) \
+	&rb->shared_data[(pointer + QB_RB_CHUNK_HEADER_WORDS) % rb->shared_hdr->word_size]
+
+#define QB_MAGIC_ASSERT(_ptr_) \
+do {							\
+	uint32_t chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, _ptr_); \
+	if (chunk_magic != QB_RB_CHUNK_MAGIC) print_header(rb); \
+	assert(chunk_magic == QB_RB_CHUNK_MAGIC); \
+} while (0)
+
+#define idx_step(idx)					\
+do {							\
+	if (idx > (rb->shared_hdr->word_size - 1)) {		\
+		idx = ((idx) % (rb->shared_hdr->word_size));	\
+	}						\
+} while (0)
+
+static void print_header(struct qb_ringbuffer_s * rb);
+static int _rb_chunk_reclaim(struct qb_ringbuffer_s * rb);
+
+qb_ringbuffer_t *
+qb_rb_open(const char *name, size_t size, uint32_t flags,
+	   size_t shared_user_data_size)
+{
+	return qb_rb_open_2(name, size, flags, shared_user_data_size, NULL);
+}
+
+qb_ringbuffer_t *
+qb_rb_open_2(const char *name, size_t size, uint32_t flags,
+	     size_t shared_user_data_size,
+	     struct qb_rb_notifier *notifiers)
+{
+	struct qb_ringbuffer_s *rb;
+	size_t real_size;
+	size_t shared_size;
+	char path[PATH_MAX];
+	int32_t fd_hdr;
+	int32_t fd_data;
+	uint32_t file_flags = O_RDWR;
+	char filename[PATH_MAX];
+	int32_t error = 0;
+	void *shm_addr;
+	long page_size = sysconf(_SC_PAGESIZE);
+
+#ifdef QB_FORCE_SHM_ALIGN
+	page_size = QB_MAX(page_size, 16 * 1024);
+#endif /* QB_FORCE_SHM_ALIGN */
+	/* The user of this api expects the 'size' parameter passed into this function
+	 * to be reflective of the max size single write we can do to the 
+	 * ringbuffer.  This means we have to add both the 'margin' space used
+	 * to calculate if there is enough space for a new chunk as well as the '+1' that
+	 * prevents overlap of the read/write pointers */
+	size += QB_RB_CHUNK_MARGIN + 1;
+	real_size = QB_ROUNDUP(size, page_size);
+
+	shared_size =
+	    sizeof(struct qb_ringbuffer_shared_s) + shared_user_data_size;
+
+	if (flags & QB_RB_FLAG_CREATE) {
+		file_flags |= O_CREAT | O_TRUNC;
+	}
+
+	rb = calloc(1, sizeof(struct qb_ringbuffer_s));
+	if (rb == NULL) {
+		return NULL;
+	}
+
+	/*
+	 * Create a shared_hdr memory segment for the header.
+	 */
+	snprintf(filename, PATH_MAX, "qb-%s-header", name);
+	fd_hdr = qb_sys_mmap_file_open(path, filename,
+				       shared_size, file_flags);
+	if (fd_hdr < 0) {
+		error = fd_hdr;
+		qb_util_log(LOG_ERR, "couldn't create file for mmap");
+		goto cleanup_hdr;
+	}
+
+	rb->shared_hdr = mmap(0,
+			      shared_size,
+			      PROT_READ | PROT_WRITE, MAP_SHARED, fd_hdr, 0);
+
+	if (rb->shared_hdr == MAP_FAILED) {
+		error = -errno;
+		qb_util_log(LOG_ERR, "couldn't create mmap for header");
+		goto cleanup_hdr;
+	}
+	qb_atomic_init();
+
+	rb->flags = flags;
+
+	/*
+	 * create the semaphore
+	 */
+	if (flags & QB_RB_FLAG_CREATE) {
+		rb->shared_data = NULL;
+		/* rb->shared_hdr->word_size tracks data by ints and not bytes/chars. */
+		rb->shared_hdr->word_size = real_size / sizeof(uint32_t);
+		rb->shared_hdr->write_pt = 0;
+		rb->shared_hdr->read_pt = 0;
+		(void)strlcpy(rb->shared_hdr->hdr_path, path, PATH_MAX);
+	}
+	if (notifiers && notifiers->post_fn) {
+		error = 0;
+		memcpy(&rb->notifier,
+		       notifiers,
+		       sizeof(struct qb_rb_notifier));
+	} else {
+		error = qb_rb_sem_create(rb, flags);
+	}
+	if (error < 0) {
+		errno = -error;
+		qb_util_perror(LOG_ERR, "couldn't create a semaphore");
+		goto cleanup_hdr;
+	}
+
+	/* Create the shared_data memory segment for the actual ringbuffer.
+	 * They have to be separate.
+	 */
+	if (flags & QB_RB_FLAG_CREATE) {
+		snprintf(filename, PATH_MAX, "qb-%s-data", name);
+		fd_data = qb_sys_mmap_file_open(path,
+						filename,
+						real_size, file_flags);
+		(void)strlcpy(rb->shared_hdr->data_path, path, PATH_MAX);
+	} else {
+		fd_data = qb_sys_mmap_file_open(path,
+						rb->shared_hdr->data_path,
+						real_size, file_flags);
+	}
+	if (fd_data < 0) {
+		error = fd_data;
+		qb_util_log(LOG_ERR, "couldn't create file for mmap");
+		goto cleanup_hdr;
+	}
+
+	qb_util_log(LOG_DEBUG,
+		    "shm size:%ld; real_size:%ld; rb->word_size:%d", size,
+		    real_size, rb->shared_hdr->word_size);
+
+	/* this function closes fd_data */
+	error = qb_sys_circular_mmap(fd_data, &shm_addr, real_size);
+	rb->shared_data = shm_addr;
+	if (error != 0) {
+		qb_util_log(LOG_ERR, "couldn't create circular mmap on %s",
+			    rb->shared_hdr->data_path);
+		goto cleanup_data;
+	}
+
+	if (flags & QB_RB_FLAG_CREATE) {
+		memset(rb->shared_data, 0, real_size);
+		rb->shared_data[rb->shared_hdr->word_size] = 5;
+		rb->shared_hdr->ref_count = 1;
+	} else {
+		qb_atomic_int_inc(&rb->shared_hdr->ref_count);
+	}
+
+	close(fd_hdr);
+	return rb;
+
+cleanup_data:
+	if (flags & QB_RB_FLAG_CREATE) {
+		unlink(rb->shared_hdr->data_path);
+	}
+
+cleanup_hdr:
+	if (fd_hdr >= 0) {
+		close(fd_hdr);
+	}
+	if (rb && (flags & QB_RB_FLAG_CREATE)) {
+		unlink(rb->shared_hdr->hdr_path);
+		if (rb->notifier.destroy_fn) {
+			(void)rb->notifier.destroy_fn(rb->notifier.instance);
+		}
+	}
+	if (rb && (rb->shared_hdr != MAP_FAILED && rb->shared_hdr != NULL)) {
+		munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s));
+	}
+	free(rb);
+	errno = -error;
+	return NULL;
+}
+
+
+void
+qb_rb_close(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return;
+	}
+	qb_enter();
+
+	(void)qb_atomic_int_dec_and_test(&rb->shared_hdr->ref_count);
+	if (rb->flags & QB_RB_FLAG_CREATE) {
+		if (rb->notifier.destroy_fn) {
+			(void)rb->notifier.destroy_fn(rb->notifier.instance);
+		}
+		unlink(rb->shared_hdr->data_path);
+		unlink(rb->shared_hdr->hdr_path);
+		qb_util_log(LOG_DEBUG,
+			    "Free'ing ringbuffer: %s",
+			    rb->shared_hdr->hdr_path);
+	} else {
+		qb_util_log(LOG_DEBUG,
+			    "Closing ringbuffer: %s", rb->shared_hdr->hdr_path);
+	}
+	munmap(rb->shared_data, (rb->shared_hdr->word_size * sizeof(uint32_t)) << 1);
+	munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s));
+	free(rb);
+}
+
+void
+qb_rb_force_close(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return;
+	}
+	qb_enter();
+
+	if (rb->notifier.destroy_fn) {
+		(void)rb->notifier.destroy_fn(rb->notifier.instance);
+	}
+
+        errno = 0;
+	unlink(rb->shared_hdr->data_path);
+	qb_util_perror(LOG_DEBUG,
+		    "Force free'ing ringbuffer: %s",
+		    rb->shared_hdr->data_path);
+
+        errno = 0;
+	unlink(rb->shared_hdr->hdr_path);
+	qb_util_perror(LOG_DEBUG,
+		    "Force free'ing ringbuffer: %s",
+		    rb->shared_hdr->hdr_path);
+	munmap(rb->shared_data, (rb->shared_hdr->word_size * sizeof(uint32_t)) << 1);
+	munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s));
+	free(rb);
+}
+
+char *
+qb_rb_name_get(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return NULL;
+	}
+	return rb->shared_hdr->hdr_path;
+}
+
+void *
+qb_rb_shared_user_data_get(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return NULL;
+	}
+	return rb->shared_hdr->user_data;
+}
+
+int32_t
+qb_rb_refcount_get(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	return qb_atomic_int_get(&rb->shared_hdr->ref_count);
+}
+
+ssize_t
+qb_rb_space_free(struct qb_ringbuffer_s * rb)
+{
+	uint32_t write_size;
+	uint32_t read_size;
+	size_t space_free = 0;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	if (rb->notifier.space_used_fn) {
+		return (rb->shared_hdr->word_size * sizeof(uint32_t)) -
+			rb->notifier.space_used_fn(rb->notifier.instance);
+	}
+	write_size = rb->shared_hdr->write_pt;
+	read_size = rb->shared_hdr->read_pt;
+
+	if (write_size > read_size) {
+		space_free =
+		    (read_size - write_size + rb->shared_hdr->word_size) - 1;
+	} else if (write_size < read_size) {
+		space_free = (read_size - write_size) - 1;
+	} else {
+		if (rb->notifier.q_len_fn && rb->notifier.q_len_fn(rb->notifier.instance) > 0) {
+			space_free = 0;
+		} else {
+			space_free = rb->shared_hdr->word_size;
+		}
+	}
+
+	/* word -> bytes */
+	return (space_free * sizeof(uint32_t));
+}
+
+ssize_t
+qb_rb_space_used(struct qb_ringbuffer_s * rb)
+{
+	uint32_t write_size;
+	uint32_t read_size;
+	size_t space_used;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	if (rb->notifier.space_used_fn) {
+		return rb->notifier.space_used_fn(rb->notifier.instance);
+	}
+	write_size = rb->shared_hdr->write_pt;
+	read_size = rb->shared_hdr->read_pt;
+
+	if (write_size > read_size) {
+		space_used = write_size - read_size;
+	} else if (write_size < read_size) {
+		space_used =
+		    (write_size - read_size + rb->shared_hdr->word_size) - 1;
+	} else {
+		space_used = 0;
+	}
+	/* word -> bytes */
+	return (space_used * sizeof(uint32_t));
+}
+
+ssize_t
+qb_rb_chunks_used(struct qb_ringbuffer_s *rb)
+{
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	if (rb->notifier.q_len_fn) {
+		return rb->notifier.q_len_fn(rb->notifier.instance);
+	}
+	return -ENOTSUP;
+}
+
+void *
+qb_rb_chunk_alloc(struct qb_ringbuffer_s * rb, size_t len)
+{
+	uint32_t write_pt;
+
+	if (rb == NULL) {
+		errno = EINVAL;
+		return NULL;
+	}
+	/*
+	 * Reclaim data if we are over writing and we need space
+	 */
+	if (rb->flags & QB_RB_FLAG_OVERWRITE) {
+		while (qb_rb_space_free(rb) < (len + QB_RB_CHUNK_MARGIN)) {
+			int rc = _rb_chunk_reclaim(rb);
+			if (rc != 0) {
+				errno = rc;
+				return NULL;
+			}
+		}
+	} else {
+		if (qb_rb_space_free(rb) < (len + QB_RB_CHUNK_MARGIN)) {
+			errno = EAGAIN;
+			return NULL;
+		}
+	}
+
+	write_pt = rb->shared_hdr->write_pt;
+	/*
+	 * insert the chunk header
+	 */
+	rb->shared_data[write_pt] = 0;
+	QB_RB_CHUNK_MAGIC_SET(rb, write_pt, QB_RB_CHUNK_MAGIC_ALLOC);
+
+	/*
+	 * return a pointer to the beginning of the chunk data
+	 */
+	return (void *)QB_RB_CHUNK_DATA_GET(rb, write_pt);
+
+}
+
+static uint32_t
+qb_rb_chunk_step(struct qb_ringbuffer_s * rb, uint32_t pointer)
+{
+	uint32_t chunk_size = QB_RB_CHUNK_SIZE_GET(rb, pointer);
+	/*
+	 * skip over the chunk header
+	 */
+	pointer += QB_RB_CHUNK_HEADER_WORDS;
+
+	/*
+	 * skip over the user's data.
+	 */
+	pointer += (chunk_size / sizeof(uint32_t));
+	/* make allowance for non-word sizes */
+	if ((chunk_size % (sizeof(uint32_t) * QB_RB_WORD_ALIGN)) != 0) {
+		pointer++;
+	}
+
+	idx_cache_line_step(pointer);
+	return pointer;
+}
+
+int32_t
+qb_rb_chunk_commit(struct qb_ringbuffer_s * rb, size_t len)
+{
+	uint32_t old_write_pt;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	/*
+	 * commit the magic & chunk_size
+	 */
+	old_write_pt = rb->shared_hdr->write_pt;
+	rb->shared_data[old_write_pt] = len;
+
+	/*
+	 * commit the new write pointer
+	 */
+	rb->shared_hdr->write_pt = qb_rb_chunk_step(rb, old_write_pt);
+	QB_RB_CHUNK_MAGIC_SET(rb, old_write_pt, QB_RB_CHUNK_MAGIC);
+
+	DEBUG_PRINTF("commit [%zd] read: %u, write: %u -> %u (%u)\n",
+		     (rb->notifier.q_len_fn ?
+		      rb->notifier.q_len_fn(rb->notifier.instance) : 0),
+		     rb->shared_hdr->read_pt,
+		     old_write_pt,
+		     rb->shared_hdr->write_pt,
+		     rb->shared_hdr->word_size);
+
+	/*
+	 * post the notification to the reader
+	 */
+	if (rb->notifier.post_fn) {
+		return rb->notifier.post_fn(rb->notifier.instance, len);
+	}
+	return 0;
+}
+
+ssize_t
+qb_rb_chunk_write(struct qb_ringbuffer_s * rb, const void *data, size_t len)
+{
+	char *dest = qb_rb_chunk_alloc(rb, len);
+	int32_t res = 0;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+
+	if (dest == NULL) {
+		return -errno;
+	}
+
+	memcpy(dest, data, len);
+
+	res = qb_rb_chunk_commit(rb, len);
+	if (res < 0) {
+		return res;
+	}
+
+	return len;
+}
+
+static int
+_rb_chunk_reclaim(struct qb_ringbuffer_s * rb)
+{
+	uint32_t old_read_pt;
+	uint32_t new_read_pt;
+	uint32_t old_chunk_size;
+	uint32_t chunk_magic;
+	int rc = 0;
+
+	old_read_pt = rb->shared_hdr->read_pt;
+	chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, old_read_pt);
+	if (chunk_magic != QB_RB_CHUNK_MAGIC) {
+		return -EINVAL;
+	}
+
+	old_chunk_size = QB_RB_CHUNK_SIZE_GET(rb, old_read_pt);
+	new_read_pt = qb_rb_chunk_step(rb, old_read_pt);
+
+	/*
+	 * clear the header
+	 */
+	rb->shared_data[old_read_pt] = 0;
+	QB_RB_CHUNK_MAGIC_SET(rb, old_read_pt, QB_RB_CHUNK_MAGIC_DEAD);
+
+	/*
+	 * set the new read pointer after clearing the header
+	 * to prevent a situation where a fast writer will write their
+	 * new chunk between setting the new read pointer and clearing the
+	 * header.
+	 */
+	rb->shared_hdr->read_pt = new_read_pt;
+
+	if (rb->notifier.reclaim_fn) {
+		rc = rb->notifier.reclaim_fn(rb->notifier.instance,
+						 old_chunk_size);
+		if (rc < 0) {
+			errno = -rc;
+			qb_util_perror(LOG_WARNING, "reclaim_fn");
+		}
+	}
+
+	DEBUG_PRINTF("reclaim [%zd]: read: %u -> %u, write: %u\n",
+		     (rb->notifier.q_len_fn ?
+		      rb->notifier.q_len_fn(rb->notifier.instance) : 0),
+		     old_read_pt,
+		     rb->shared_hdr->read_pt,
+		     rb->shared_hdr->write_pt);
+
+	return rc;
+}
+
+void
+qb_rb_chunk_reclaim(struct qb_ringbuffer_s * rb)
+{
+	if (rb == NULL) {
+		return;
+	}
+	_rb_chunk_reclaim(rb);
+}
+
+ssize_t
+qb_rb_chunk_peek(struct qb_ringbuffer_s * rb, void **data_out, int32_t timeout)
+{
+	uint32_t read_pt;
+	uint32_t chunk_size;
+	uint32_t chunk_magic;
+	int32_t res = 0;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	if (rb->notifier.timedwait_fn) {
+		res = rb->notifier.timedwait_fn(rb->notifier.instance, timeout);
+	}
+	if (res < 0 && res != -EIDRM) {
+		if (res == -ETIMEDOUT) {
+			return 0;
+		} else {
+			errno = -res;
+			qb_util_perror(LOG_ERR, "sem_timedwait");
+		}
+		return res;
+	}
+	read_pt = rb->shared_hdr->read_pt;
+	chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, read_pt);
+	if (chunk_magic != QB_RB_CHUNK_MAGIC) {
+		if (rb->notifier.post_fn) {
+			(void)rb->notifier.post_fn(rb->notifier.instance, res);
+		}
+		return 0;
+	}
+	chunk_size = QB_RB_CHUNK_SIZE_GET(rb, read_pt);
+	*data_out = QB_RB_CHUNK_DATA_GET(rb, read_pt);
+	return chunk_size;
+}
+
+ssize_t
+qb_rb_chunk_read(struct qb_ringbuffer_s * rb, void *data_out, size_t len,
+		 int32_t timeout)
+{
+	uint32_t read_pt;
+	uint32_t chunk_size;
+	uint32_t chunk_magic;
+	int32_t res = 0;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	if (rb->notifier.timedwait_fn) {
+		res = rb->notifier.timedwait_fn(rb->notifier.instance, timeout);
+	}
+	if (res < 0 && res != -EIDRM) {
+		if (res != -ETIMEDOUT) {
+			errno = -res;
+			qb_util_perror(LOG_ERR, "sem_timedwait");
+		}
+		return res;
+	}
+
+	read_pt = rb->shared_hdr->read_pt;
+	chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, read_pt);
+
+	if (chunk_magic != QB_RB_CHUNK_MAGIC) {
+		if (rb->notifier.timedwait_fn == NULL) {
+			return -ETIMEDOUT;
+		} else {
+			(void)rb->notifier.post_fn(rb->notifier.instance, res);
+#ifdef EBADMSG
+			return -EBADMSG;
+#else
+			return -EINVAL;
+#endif
+		}
+	}
+
+	chunk_size = QB_RB_CHUNK_SIZE_GET(rb, read_pt);
+	if (len < chunk_size) {
+		qb_util_log(LOG_ERR,
+			    "trying to recv chunk of size %d but %d available",
+			    len, chunk_size);
+		if (rb->notifier.post_fn) {
+			(void)rb->notifier.post_fn(rb->notifier.instance, chunk_size);
+		}
+		return -ENOBUFS;
+	}
+
+	memcpy(data_out,
+	       QB_RB_CHUNK_DATA_GET(rb, read_pt),
+	       chunk_size);
+
+	_rb_chunk_reclaim(rb);
+
+	return chunk_size;
+}
+
+static void
+print_header(struct qb_ringbuffer_s * rb)
+{
+	printf("Ringbuffer: \n");
+	if (rb->flags & QB_RB_FLAG_OVERWRITE) {
+		printf(" ->OVERWRITE\n");
+	} else {
+		printf(" ->NORMAL\n");
+	}
+	printf(" ->write_pt [%d]\n", rb->shared_hdr->write_pt);
+	printf(" ->read_pt [%d]\n", rb->shared_hdr->read_pt);
+	printf(" ->size [%d words]\n", rb->shared_hdr->word_size);
+#ifndef S_SPLINT_S
+	printf(" =>free [%zu bytes]\n", qb_rb_space_free(rb));
+	printf(" =>used [%zu bytes]\n", qb_rb_space_used(rb));
+#endif /* S_SPLINT_S */
+}
+
+/*
+ * FILE HEADER ORDER
+ * 1. word_size
+ * 2. write_pt
+ * 3. read_pt
+ * 4. version
+ * 5. header_hash
+ *
+ * 6. data
+ */
+
+ssize_t
+qb_rb_write_to_file(struct qb_ringbuffer_s * rb, int32_t fd)
+{
+	ssize_t result;
+	ssize_t written_size = 0;
+	uint32_t hash = 0;
+	uint32_t version = QB_RB_FILE_HEADER_VERSION;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	print_header(rb);
+
+	/*
+ 	 * 1. word_size
+ 	 */
+	result = write(fd, &rb->shared_hdr->word_size, sizeof(uint32_t));
+	if (result != sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+
+	/*
+	 * 2. 3. store the read & write pointers
+	 */
+	result = write(fd, (void *)&rb->shared_hdr->write_pt, sizeof(uint32_t));
+	if (result != sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+	result = write(fd, (void *)&rb->shared_hdr->read_pt, sizeof(uint32_t));
+	if (result != sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+
+	/*
+	 * 4. version used
+	 */
+	result = write(fd, &version, sizeof(uint32_t));
+	if (result != sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+
+	/*
+	 * 5. hash helps us verify header is not corrupted on file read
+	 */
+	hash = rb->shared_hdr->word_size + rb->shared_hdr->write_pt + rb->shared_hdr->read_pt + QB_RB_FILE_HEADER_VERSION;
+	result = write(fd, &hash, sizeof(uint32_t));
+	if (result != sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+
+	result = write(fd, rb->shared_data,
+		       rb->shared_hdr->word_size * sizeof(uint32_t));
+	if (result != rb->shared_hdr->word_size * sizeof(uint32_t)) {
+		return -errno;
+	}
+	written_size += result;
+
+	qb_util_log(LOG_DEBUG, " writing total of: %zd\n", written_size);
+
+	return written_size;
+}
+
+qb_ringbuffer_t *
+qb_rb_create_from_file(int32_t fd, uint32_t flags)
+{
+	ssize_t n_read;
+	size_t n_required;
+	size_t total_read = 0;
+	uint32_t read_pt;
+	uint32_t write_pt;
+	struct qb_ringbuffer_s *rb;
+	uint32_t word_size = 0;
+	uint32_t version = 0;
+	uint32_t hash = 0;
+	uint32_t calculated_hash = 0;
+
+	if (fd < 0) {
+		return NULL;
+	}
+
+	/*
+	 * 1. word size
+	 */
+	n_required = sizeof(uint32_t);
+	n_read = read(fd, &word_size, n_required);
+	if (n_read != n_required) {
+		qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
+		return NULL;
+	}
+	total_read += n_read;
+
+	/*
+	 * 2. 3. read & write pointers
+	 */
+	n_read = read(fd, &write_pt, sizeof(uint32_t));
+	assert(n_read == sizeof(uint32_t));
+	total_read += n_read;
+
+	n_read = read(fd, &read_pt, sizeof(uint32_t));
+	assert(n_read == sizeof(uint32_t));
+	total_read += n_read;
+
+	/*
+	 * 4. version
+	 */
+	n_required = sizeof(uint32_t);
+	n_read = read(fd, &version, n_required);
+	if (n_read != n_required) {
+		qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
+		return NULL;
+	}
+	total_read += n_read;
+
+	/*
+	 * 5. Hash
+	 */
+	n_required = sizeof(uint32_t);
+	n_read = read(fd, &hash, n_required);
+	if (n_read != n_required) {
+		qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
+		return NULL;
+	}
+	total_read += n_read;
+
+	calculated_hash = word_size + write_pt + read_pt + version;
+	if (hash != calculated_hash) {
+		qb_util_log(LOG_ERR, "Corrupt blackbox: File header hash (%d) does not match calculated hash (%d)", hash, calculated_hash);
+		return NULL;
+	} else if (version != QB_RB_FILE_HEADER_VERSION) {
+		qb_util_log(LOG_ERR, "Wrong file header version. Expected %d got %d",
+			QB_RB_FILE_HEADER_VERSION, version);
+		return NULL;
+	}
+
+	/*
+	 * 6. data
+	 */
+	n_required = (word_size * sizeof(uint32_t));
+
+	/*
+	 * qb_rb_open adds QB_RB_CHUNK_MARGIN + 1 to the requested size.
+	 */
+	rb = qb_rb_open("create_from_file", n_required - (QB_RB_CHUNK_MARGIN + 1),
+			QB_RB_FLAG_CREATE | QB_RB_FLAG_NO_SEMAPHORE, 0);
+	if (rb == NULL) {
+		return NULL;
+	}
+	rb->shared_hdr->read_pt = read_pt;
+	rb->shared_hdr->write_pt = write_pt;
+
+	n_read = read(fd, rb->shared_data, n_required);
+	if (n_read < 0) {
+		qb_util_perror(LOG_ERR, "Unable to read blackbox file data");
+		goto cleanup_fail;
+	}
+	total_read += n_read;
+
+	if (n_read != n_required) {
+		qb_util_log(LOG_WARNING, "read %zd bytes, but expected %zu",
+			    n_read, n_required);
+		goto cleanup_fail;
+	}
+
+	qb_util_log(LOG_DEBUG, "read total of: %zd", total_read);
+	print_header(rb);
+
+	return rb;
+
+cleanup_fail:
+	qb_rb_close(rb);
+	return NULL;
+}
+
+int32_t
+qb_rb_chown(struct qb_ringbuffer_s * rb, uid_t owner, gid_t group)
+{
+	int32_t res;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	res = chown(rb->shared_hdr->data_path, owner, group);
+	if (res < 0 && errno != EPERM) {
+		return -errno;
+	}
+	res = chown(rb->shared_hdr->hdr_path, owner, group);
+	if (res < 0 && errno != EPERM) {
+		return -errno;
+	}
+	return 0;
+}
+
+int32_t
+qb_rb_chmod(qb_ringbuffer_t * rb, mode_t mode)
+{
+	int32_t res;
+
+	if (rb == NULL) {
+		return -EINVAL;
+	}
+	res = chmod(rb->shared_hdr->data_path, mode);
+	if (res < 0) {
+		return -errno;
+	}
+	res = chmod(rb->shared_hdr->hdr_path, mode);
+	if (res < 0) {
+		return -errno;
+	}
+	return 0;
+}
diff --git a/lib/ringbuffer_helper.c b/lib/ringbuffer_helper.c
new file mode 100644
index 0000000..455fd99
--- /dev/null
+++ b/lib/ringbuffer_helper.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "ringbuffer_int.h"
+#include <qb/qbdefs.h>
+
+static int32_t
+my_posix_sem_timedwait(void * instance, int32_t ms_timeout)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	struct timespec ts_timeout;
+	int32_t res;
+
+	if (ms_timeout > 0) {
+		qb_util_timespec_from_epoch_get(&ts_timeout);
+		qb_timespec_add_ms(&ts_timeout, ms_timeout);
+	}
+
+sem_wait_again:
+	if (ms_timeout > 0) {
+		res = rpl_sem_timedwait(&rb->shared_hdr->posix_sem, &ts_timeout);
+	} else if (ms_timeout == 0) {
+		res = rpl_sem_trywait(&rb->shared_hdr->posix_sem);
+	} else {
+		res = rpl_sem_wait(&rb->shared_hdr->posix_sem);
+	}
+	if (res == -1) {
+		switch (errno) {
+		case EINTR:
+			goto sem_wait_again;
+			break;
+		case EAGAIN:
+			res = -ETIMEDOUT;
+			break;
+		case ETIMEDOUT:
+			res = -errno;
+			break;
+		default:
+			res = -errno;
+			qb_util_perror(LOG_ERR, "error waiting for semaphore");
+			break;
+		}
+	}
+	return res;
+}
+
+static int32_t
+my_posix_sem_post(void * instance, size_t msg_size)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	if (rpl_sem_post(&rb->shared_hdr->posix_sem) < 0) {
+		return -errno;
+	} else {
+		return 0;
+	}
+}
+
+static ssize_t
+my_posix_getvalue_fn(void * instance)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	int val;
+	if (rpl_sem_getvalue(&rb->shared_hdr->posix_sem, &val) < 0) {
+		return -errno;
+	} else {
+		return val;
+	}
+}
+
+static int32_t
+my_posix_sem_destroy(void * instance)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	qb_enter();
+	if (rpl_sem_destroy(&rb->shared_hdr->posix_sem) == -1) {
+		return -errno;
+	} else {
+		return 0;
+	}
+}
+
+static int32_t
+my_posix_sem_create(void * instance, uint32_t flags)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	int32_t pshared = QB_FALSE;
+	if (flags & QB_RB_FLAG_SHARED_PROCESS) {
+		if ((flags & QB_RB_FLAG_CREATE) == 0) {
+			return 0;
+		}
+		pshared = QB_TRUE;
+	}
+	if (rpl_sem_init(&rb->shared_hdr->posix_sem, pshared, 0) == -1) {
+		return -errno;
+	} else {
+		return 0;
+	}
+}
+
+static int32_t
+my_sysv_sem_timedwait(void * instance, int32_t ms_timeout)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	struct sembuf sops[1];
+	int32_t res = 0;
+#ifdef HAVE_SEMTIMEDOP
+	struct timespec ts_timeout;
+	struct timespec *ts_pt;
+
+	if (ms_timeout >= 0) {
+		/*
+		 * Note: sem_timedwait takes an absolute time where as semtimedop
+		 * takes a relative time.
+		 */
+		ts_timeout.tv_sec = 0;
+		ts_timeout.tv_nsec = 0;
+		qb_timespec_add_ms(&ts_timeout, ms_timeout);
+		ts_pt = &ts_timeout;
+	} else {
+		ts_pt = NULL;
+	}
+#endif /* HAVE_SEMTIMEDOP */
+
+	/*
+	 * wait for sem post.
+	 */
+	sops[0].sem_num = 0;
+	sops[0].sem_op = -1;
+#ifdef HAVE_SEMTIMEDOP
+	sops[0].sem_flg = 0;
+#else
+	sops[0].sem_flg = IPC_NOWAIT;
+#endif /* HAVE_SEMTIMEDOP */
+
+semop_again:
+#ifdef HAVE_SEMTIMEDOP
+	if (semtimedop(rb->sem_id, sops, 1, ts_pt) == -1)
+#else
+	if (semop(rb->sem_id, sops, 1) == -1)
+#endif /* HAVE_SEMTIMEDOP */
+	{
+		if (errno == EINTR) {
+			goto semop_again;
+		} else if (errno == EAGAIN) {
+			/* make consistent with sem_timedwait */
+			res = -ETIMEDOUT;
+		} else {
+			res = -errno;
+			qb_util_perror(LOG_ERR, "error waiting for semaphore");
+		}
+		return res;
+	}
+	return 0;
+}
+
+static int32_t
+my_sysv_sem_post(void * instance, size_t msg_size)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	struct sembuf sops[1];
+
+	if ((rb->flags & QB_RB_FLAG_SHARED_PROCESS) == 0) {
+		return 0;
+	}
+
+	sops[0].sem_num = 0;
+	sops[0].sem_op = 1;
+	sops[0].sem_flg = 0;
+
+semop_again:
+	if (semop(rb->sem_id, sops, 1) == -1) {
+		if (errno == EINTR) {
+			goto semop_again;
+		} else {
+			qb_util_perror(LOG_ERR,
+				       "could not increment semaphore");
+		}
+
+		return -errno;
+	}
+	return 0;
+}
+
+static ssize_t
+my_sysv_getvalue_fn(void * instance)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	ssize_t res = semctl(rb->sem_id, 0, GETVAL, 0);
+	if (res == -1) {
+		return -errno;
+	}
+	return res;
+}
+
+static int32_t
+my_sysv_sem_destroy(void * instance)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	if (semctl(rb->sem_id, 0, IPC_RMID, 0) == -1) {
+		return -errno;
+	} else {
+		return 0;
+	}
+}
+
+static int32_t
+my_sysv_sem_create(void * instance, uint32_t flags)
+{
+	struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
+	union semun options;
+	int32_t res;
+	key_t sem_key;
+
+	sem_key = ftok(rb->shared_hdr->hdr_path, (rb->shared_hdr->word_size + 1));
+
+	if (sem_key == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "couldn't get a sem id");
+		return res;
+	}
+
+	if (flags & QB_RB_FLAG_CREATE) {
+		rb->sem_id = semget(sem_key, 1, IPC_CREAT | IPC_EXCL | 0600);
+		if (rb->sem_id == -1) {
+			res = -errno;
+			qb_util_perror(LOG_ERR, "couldn't create a semaphore");
+			return res;
+		}
+		options.val = 0;
+		res = semctl(rb->sem_id, 0, SETVAL, options);
+	} else {
+		rb->sem_id = semget(sem_key, 0, 0600);
+		if (rb->sem_id == -1) {
+			res = -errno;
+			qb_util_perror(LOG_ERR, "couldn't get a sem id");
+			return res;
+		}
+		res = 0;
+	}
+	qb_util_log(LOG_DEBUG, "sem key:%d, id:%d, value:%d",
+		    (int)sem_key, rb->sem_id, semctl(rb->sem_id, 0, GETVAL, 0));
+
+	return res;
+}
+
+int32_t
+qb_rb_sem_create(struct qb_ringbuffer_s * rb, uint32_t flags)
+{
+	int32_t rc;
+	int32_t use_posix = QB_TRUE;
+
+	if ((flags & QB_RB_FLAG_SHARED_PROCESS) &&
+	    !(flags & QB_RB_FLAG_NO_SEMAPHORE)) {
+#if defined(HAVE_POSIX_PSHARED_SEMAPHORE) || \
+    defined(HAVE_RPL_PSHARED_SEMAPHORE)
+		use_posix = QB_TRUE;
+#else
+	#ifdef HAVE_SYSV_PSHARED_SEMAPHORE
+		use_posix = QB_FALSE;
+	#else
+		return -ENOTSUP;
+	#endif /* HAVE_SYSV_PSHARED_SEMAPHORE */
+#endif /* HAVE_POSIX_PSHARED_SEMAPHORE */
+	}
+	if (flags & QB_RB_FLAG_NO_SEMAPHORE) {
+		rc = 0;
+		rb->notifier.instance = NULL;
+		rb->notifier.timedwait_fn = NULL;
+		rb->notifier.post_fn = NULL;
+		rb->notifier.q_len_fn = NULL;
+		rb->notifier.space_used_fn = NULL;
+		rb->notifier.destroy_fn = NULL;
+	} else if (use_posix) {
+		rc = my_posix_sem_create(rb, flags);
+		rb->notifier.instance = rb;
+		rb->notifier.timedwait_fn = my_posix_sem_timedwait;
+		rb->notifier.post_fn = my_posix_sem_post;
+		rb->notifier.q_len_fn = my_posix_getvalue_fn;
+		rb->notifier.space_used_fn = NULL;
+		rb->notifier.destroy_fn = my_posix_sem_destroy;
+	} else {
+		rc = my_sysv_sem_create(rb, flags);
+		rb->notifier.instance = rb;
+		rb->notifier.timedwait_fn = my_sysv_sem_timedwait;
+		rb->notifier.post_fn = my_sysv_sem_post;
+		rb->notifier.q_len_fn = my_sysv_getvalue_fn;
+		rb->notifier.space_used_fn = NULL;
+		rb->notifier.destroy_fn = my_sysv_sem_destroy;
+	}
+	return rc;
+}
diff --git a/lib/ringbuffer_int.h b/lib/ringbuffer_int.h
new file mode 100644
index 0000000..f76593e
--- /dev/null
+++ b/lib/ringbuffer_int.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _RINGBUFFER_H_
+#define _RINGBUFFER_H_
+
+#include "os_base.h"
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif /* HAVE_SYS_MMAN_H */
+#ifdef HAVE_SYS_SEM_H
+#include <sys/sem.h>
+#endif
+#ifdef HAVE_SYS_IPC_H
+#include <sys/ipc.h>
+#endif
+#include "rpl_sem.h"
+#include "util_int.h"
+#include <qb/qbutil.h>
+#include <qb/qbrb.h>
+
+struct qb_ringbuffer_s;
+
+int32_t qb_rb_sem_create(struct qb_ringbuffer_s *rb, uint32_t flags);
+
+typedef int32_t(*qb_rb_notifier_post_fn_t) (void * instance, size_t msg_size);
+typedef ssize_t(*qb_rb_notifier_q_len_fn_t) (void * instance);
+typedef ssize_t(*qb_rb_notifier_used_fn_t) (void * instance);
+typedef int32_t(*qb_rb_notifier_timedwait_fn_t) (void * instance,
+					         int32_t ms_timeout);
+typedef int32_t(*qb_rb_notifier_reclaim_fn_t) (void * instance, size_t msg_size);
+typedef int32_t(*qb_rb_notifier_destroy_fn_t) (void * instance);
+
+struct qb_rb_notifier {
+	qb_rb_notifier_post_fn_t post_fn;
+	qb_rb_notifier_q_len_fn_t q_len_fn;
+	qb_rb_notifier_used_fn_t space_used_fn;
+	qb_rb_notifier_timedwait_fn_t timedwait_fn;
+	qb_rb_notifier_reclaim_fn_t reclaim_fn;
+	qb_rb_notifier_destroy_fn_t destroy_fn;
+	void *instance;
+};
+
+struct qb_ringbuffer_shared_s {
+	volatile uint32_t write_pt;
+	volatile uint32_t read_pt;
+	uint32_t word_size;
+	char hdr_path[PATH_MAX];
+	char data_path[PATH_MAX];
+	int32_t ref_count;
+	rpl_sem_t posix_sem;
+	char user_data[1];
+} __attribute__ ((aligned(8)));
+
+struct qb_ringbuffer_s {
+	uint32_t flags;
+	int32_t sem_id;
+	struct qb_ringbuffer_shared_s *shared_hdr;
+	uint32_t *shared_data;
+
+	struct qb_rb_notifier notifier;
+};
+
+void qb_rb_force_close(qb_ringbuffer_t * rb);
+
+qb_ringbuffer_t *qb_rb_open_2(const char *name, size_t size, uint32_t flags,
+			      size_t shared_user_data_size,
+			      struct qb_rb_notifier *notifier);
+
+
+#ifndef HAVE_SEMUN
+union semun {
+	int32_t val;
+	struct semid_ds *buf;
+	unsigned short int *array;
+	struct seminfo *__buf;
+};
+#endif /* HAVE_SEMUN */
+
+#endif /* _RINGBUFFER_H_ */
diff --git a/lib/rpl_sem.c b/lib/rpl_sem.c
new file mode 100644
index 0000000..7daeb4f
--- /dev/null
+++ b/lib/rpl_sem.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2007 Pingtel Corp., certain elements licensed under a Contributor Agreement.
+ * Contributors retain copyright to elements licensed under a Contributor Agreement.
+ * Licensed to the User under the LGPL license.
+ *
+ * Modified by: Angus Salkeld <asalkeld at redhat.com>
+ *              Copyright (C) 2012 Red Hat, Inc.
+ * To conform to posix API and support process shared semaphores.
+ *
+ * The bsd posix semaphore implementation does not have support for timing
+ * out while waiting for a synchronization object. This uses the
+ * pthread_cond_timedwait function and a mutex to build all the other
+ * synchronization objecs with timeout capabilities.
+ */
+
+#include "os_base.h"
+#include <pthread.h>
+#include "rpl_sem.h"
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+
+int
+rpl_sem_init(rpl_sem_t * sem, int pshared, unsigned int count)
+{
+	int rc;
+	pthread_mutexattr_t mattr;
+	pthread_condattr_t cattr;
+
+#ifndef HAVE_RPL_PSHARED_SEMAPHORE
+	if (pshared) {
+		errno = ENOSYS;
+		return -1;
+	}
+#endif /* HAVE_RPL_PSHARED_SEMAPHORE */
+	sem->count = count;
+	sem->destroy_request = QB_FALSE;
+
+	(void)pthread_mutexattr_init(&mattr);
+	(void)pthread_condattr_init(&cattr);
+#ifdef HAVE_RPL_PSHARED_SEMAPHORE
+	if (pshared) {
+		rc = pthread_mutexattr_setpshared(&mattr,
+						  PTHREAD_PROCESS_SHARED);
+		if (rc != 0) {
+			goto cleanup;
+		}
+		rc = pthread_condattr_setpshared(&cattr,
+						 PTHREAD_PROCESS_SHARED);
+		if (rc != 0) {
+			goto cleanup;
+		}
+	}
+#endif /* HAVE_RPL_PSHARED_SEMAPHORE */
+	rc = pthread_mutex_init(&sem->mutex, &mattr);
+	if (rc != 0) {
+		goto cleanup;
+	}
+	rc = pthread_cond_init(&sem->cond, &cattr);
+
+	if (rc != 0) {
+		goto cleanup_mutex;
+	}
+	return 0;
+
+cleanup_mutex:
+	pthread_mutex_destroy(&sem->mutex);
+
+cleanup:
+	pthread_mutexattr_destroy(&mattr);
+	pthread_condattr_destroy(&cattr);
+	return rc;
+}
+
+static int
+_rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout)
+{
+	int retval = pthread_mutex_lock(&sem->mutex);
+	if (retval != 0) {
+		return -errno;
+	}
+	if (sem->destroy_request) {
+		retval = -EINVAL;
+		goto unlock_it;
+	}
+
+	/* wait for sem->count to be not zero, or error
+	 */
+	while (0 == retval && !sem->count) {
+		retval = -pthread_cond_timedwait(&sem->cond,
+						 &sem->mutex, timeout);
+	}
+	if (sem->destroy_request) {
+		retval = -EINVAL;
+		goto unlock_it;
+	}
+
+	switch (retval) {
+	case 0:
+		/* retval is 0 and sem->count is not, the sem is ours
+		 */
+		sem->count--;
+		break;
+
+	case ETIMEDOUT:
+		/* timedout waiting for count to be not zero
+		 */
+		retval = -EAGAIN;
+		break;
+
+	default:
+		break;
+	}
+
+unlock_it:
+	pthread_mutex_unlock(&sem->mutex);
+	return retval;
+}
+
+int
+rpl_sem_wait(rpl_sem_t * sem)
+{
+
+	struct timespec ts_timeout;
+	int32_t rc;
+
+	do {
+		qb_util_timespec_from_epoch_get(&ts_timeout);
+		qb_timespec_add_ms(&ts_timeout, 1000);
+		rc = _rpl_sem_timedwait(sem, &ts_timeout);
+	} while (rc == -EAGAIN);
+	if (rc < 0) {
+		errno = -rc;
+		return -1;
+	}
+	return 0;
+}
+
+int
+rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout)
+{
+	int rc = _rpl_sem_timedwait(sem, timeout);
+	if (rc < 0) {
+		errno = -rc;
+		return -1;
+	}
+	return 0;
+}
+
+int
+rpl_sem_trywait(rpl_sem_t * sem)
+{
+	int retval = pthread_mutex_lock(&sem->mutex);
+	if (retval != 0) {
+		errno = retval;
+		return -1;
+	}
+	if (sem->count) {
+		sem->count--;
+		pthread_mutex_unlock(&sem->mutex);
+		return 0;
+	}
+	errno = EAGAIN;
+	pthread_mutex_unlock(&sem->mutex);
+	return -1;
+}
+
+int
+rpl_sem_post(rpl_sem_t * sem)
+{
+	int retval = pthread_mutex_lock(&sem->mutex);
+	if (retval != 0) {
+		errno = retval;
+		return -1;
+	}
+	sem->count++;
+	retval = pthread_cond_broadcast(&sem->cond);
+	pthread_mutex_unlock(&sem->mutex);
+	if (retval != 0) {
+		errno = retval;
+		return -1;
+	}
+	return 0;
+}
+
+int
+rpl_sem_getvalue(rpl_sem_t * sem, int *sval)
+{
+	int retval = pthread_mutex_lock(&sem->mutex);
+	if (retval != 0) {
+		errno = retval;
+		return -1;
+	}
+	*sval = sem->count;
+	pthread_mutex_unlock(&sem->mutex);
+	return 0;
+}
+
+int
+rpl_sem_destroy(rpl_sem_t * sem)
+{
+	int retval = pthread_mutex_lock(&sem->mutex);
+	if (retval != 0) {
+		errno = retval;
+		return -1;
+	}
+	sem->destroy_request = QB_TRUE;
+	pthread_mutex_unlock(&sem->mutex);
+	(void)pthread_cond_broadcast(&sem->cond);
+
+	(void)pthread_cond_destroy(&sem->cond);
+	(void)pthread_mutex_destroy(&sem->mutex);
+
+	return 0;
+}
diff --git a/lib/rpl_sem.h b/lib/rpl_sem.h
new file mode 100644
index 0000000..fb8005e
--- /dev/null
+++ b/lib/rpl_sem.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 Pingtel Corp., certain elements licensed under a Contributor Agreement.
+ * Contributors retain copyright to elements licensed under a Contributor Agreement.
+ * Licensed to the User under the LGPL license.
+ *
+ * Modified by: Angus Salkeld <asalkeld at redhat.com>
+ *              Copyright (C) 2012 Red Hat, Inc.
+ * To conform to official implementation and support process shared semaphores.
+ *
+ * The bsd posix semaphore implementation does not have support for timing
+ * out while waiting for a synchronization object. This uses the
+ * pthread_cond_timedwait function and a mutex to build all the other
+ * synchronization objecs with timeout capabilities.
+ */
+
+#ifndef _RPL_SEM_H
+#define _RPL_SEM_H
+
+#include "os_base.h"
+#include <pthread.h>
+#include <semaphore.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_SEM_TIMEDWAIT
+#define rpl_sem_t sem_t
+#define rpl_sem_init sem_init
+#define rpl_sem_wait sem_wait
+#define rpl_sem_timedwait sem_timedwait
+#define rpl_sem_post sem_post
+#define rpl_sem_getvalue sem_getvalue
+#define rpl_sem_destroy sem_destroy
+#define rpl_sem_trywait sem_trywait
+#else
+
+typedef struct rpl_sem {
+	unsigned int count;
+	uint32_t destroy_request;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+} rpl_sem_t;
+
+int rpl_sem_init(rpl_sem_t * sem, int pshared, unsigned int count);
+
+int rpl_sem_wait(rpl_sem_t * sem);
+
+int rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout);
+
+int rpl_sem_trywait(rpl_sem_t * sem);
+
+int rpl_sem_post(rpl_sem_t * sem);
+
+int rpl_sem_getvalue(rpl_sem_t * sem, int *sval);
+
+int rpl_sem_destroy(rpl_sem_t * sem);
+
+#endif /* HAVE_SEM_TIMEDWAIT */
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* _RPL_SEM_H */
diff --git a/lib/skiplist.c b/lib/skiplist.c
new file mode 100644
index 0000000..a20fa23
--- /dev/null
+++ b/lib/skiplist.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <os_base.h>
+#include <assert.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbmap.h>
+#include "map_int.h"
+
+#define SKIPLIST_LEVEL_MAX 8
+#define SKIPLIST_LEVEL_MIN 0
+/* The amount of possible levels */
+#define SKIPLIST_LEVEL_COUNT (SKIPLIST_LEVEL_MAX - SKIPLIST_LEVEL_MIN + 1)
+
+struct skiplist_iter {
+	struct qb_map_iter i;
+	struct skiplist_node *n;
+};
+
+struct skiplist_node {
+	const char *key;
+	void *value;
+	int8_t level;
+	uint32_t refcount;
+	struct qb_list_head notifier_head;
+
+	/* An array of @level + 1 node pointers */
+	struct skiplist_node **forward;
+};
+
+struct skiplist {
+	struct qb_map map;
+
+	size_t length;
+	int8_t level;
+	struct skiplist_node *header;
+};
+
+/* An array of nodes that need to be updated after an insert or delete operation
+ */
+typedef struct skiplist_node *skiplist_update_t[SKIPLIST_LEVEL_COUNT];
+
+static int8_t
+skiplist_level_generate(void)
+{
+	/* This constant is found by 1 / P, where P = 0.25. */
+	enum { P_INVERSE = 4 };
+
+	/* The original algorithm's random number is in the range [0, 1), so
+	 * max M = 1. Its ceiling C = M * P = 1 * P = P.
+	 *
+	 * Our random number is in the range [0, UINT16_MAX], so M = UINT16_MAX. Therefore,
+	 * C = UINT16_MAX * P = UINT16_MAX / P_INVERSE.
+	 */
+	enum { P_CEIL = UINT16_MAX / P_INVERSE };
+	int8_t level = SKIPLIST_LEVEL_MIN;
+
+	while ((uint16_t) (random()) < P_CEIL)
+		level++;
+
+	if (level < SKIPLIST_LEVEL_MAX)
+		return level;
+
+	return SKIPLIST_LEVEL_MAX;
+}
+
+static struct skiplist_node *
+skiplist_node_next(const struct skiplist_node *node)
+{
+	const struct skiplist_node *n = node;
+	do {
+		n = n->forward[SKIPLIST_LEVEL_MIN];
+	} while (n && n->refcount == 0);
+	return (struct skiplist_node *)n;
+}
+
+/*
+ * Create a new level @level node with value @value. The node should eventually
+ * be destroyed with @skiplist_node_destroy.
+ *
+ * return: a new node on success and NULL otherwise.
+ */
+static struct skiplist_node *
+skiplist_node_new(const int8_t level, const char *key, const void *value)
+{
+	struct skiplist_node *new_node = (struct skiplist_node *)
+	    (malloc(sizeof(struct skiplist_node)));
+
+	if (!new_node)
+		return NULL;
+
+	new_node->value = (void *)value;
+	new_node->key = key;
+	new_node->level = level;
+	new_node->refcount = 1;
+	qb_list_init(&new_node->notifier_head);
+
+	/* A level 0 node still needs to hold 1 forward pointer, etc. */
+	new_node->forward = (struct skiplist_node **)
+	    (calloc(level + 1, sizeof(struct skiplist_node *)));
+
+	if (new_node->forward == NULL) {
+		free(new_node);
+		return NULL;
+	}
+
+	return new_node;
+}
+
+static struct skiplist_node *
+skiplist_header_node_new(void)
+{
+	return skiplist_node_new(SKIPLIST_LEVEL_MAX, NULL, NULL);
+}
+
+/* An operation to perform after comparing a user value or search with a
+ * node's value
+ */
+typedef enum {
+	OP_GOTO_NEXT_LEVEL,
+	OP_GOTO_NEXT_NODE,
+	OP_FINISH,
+} op_t;
+
+static op_t
+op_search(const struct skiplist *list,
+	  const struct skiplist_node *fwd_node, const void *search)
+{
+	int32_t cmp;
+
+	if (!fwd_node)
+		return OP_GOTO_NEXT_LEVEL;
+
+	cmp = strcmp(fwd_node->key, search);
+	if (cmp < 0) {
+		return OP_GOTO_NEXT_NODE;
+	} else if (cmp == 0) {
+		return OP_FINISH;
+	}
+
+	return OP_GOTO_NEXT_LEVEL;
+}
+
+static struct skiplist_node *
+skiplist_lookup(struct skiplist *list, const char *key)
+{
+	struct skiplist_node *cur_node = list->header;
+	int8_t level = list->level;
+
+	while (level >= SKIPLIST_LEVEL_MIN) {
+		struct skiplist_node *fwd_node = cur_node->forward[level];
+
+		switch (op_search(list, fwd_node, key)) {
+		case OP_FINISH:
+			return fwd_node;
+		case OP_GOTO_NEXT_NODE:
+			cur_node = fwd_node;
+			break;
+		case OP_GOTO_NEXT_LEVEL:
+			level--;
+		}
+	}
+
+	return NULL;
+}
+
+static void
+skiplist_notify(struct skiplist *l, struct skiplist_node *n,
+		uint32_t event, char *key, void *old_value, void *value)
+{
+	struct qb_list_head *list;
+	struct qb_map_notifier *tn;
+
+	/* node callbacks
+	 */
+	qb_list_for_each(list, &n->notifier_head) {
+		tn = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (tn->events & event) {
+			tn->callback(event, key, old_value, value,
+				     tn->user_data);
+		}
+	}
+	/* global callbacks
+	 */
+	qb_list_for_each(list, &l->header->notifier_head) {
+		tn = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (tn->events & event) {
+			tn->callback(event, key, old_value, value,
+				     tn->user_data);
+		}
+		if (((event & QB_MAP_NOTIFY_DELETED) ||
+		     (event & QB_MAP_NOTIFY_REPLACED)) &&
+		    (tn->events & QB_MAP_NOTIFY_FREE)) {
+			tn->callback(QB_MAP_NOTIFY_FREE, (char *)key,
+				     old_value, value, tn->user_data);
+		}
+	}
+
+}
+
+static void
+skiplist_node_destroy(struct skiplist_node *node, struct skiplist *list)
+{
+	struct qb_list_head *pos;
+	struct qb_list_head *next;
+	struct qb_map_notifier *tn;
+
+	skiplist_notify(list, node,
+			QB_MAP_NOTIFY_DELETED,
+			(char *)node->key, node->value, NULL);
+
+	qb_list_for_each_safe(pos, next, &node->notifier_head) {
+		tn = qb_list_entry(pos, struct qb_map_notifier, list);
+		qb_list_del(&tn->list);
+		free(tn);
+	}
+
+	free(node->forward);
+	free(node);
+}
+
+static void
+skiplist_node_deref(struct skiplist_node *node, struct skiplist *list)
+{
+	node->refcount--;
+	if (node->refcount == 0) {
+		skiplist_node_destroy(node, list);
+	}
+}
+
+static int32_t
+skiplist_notify_add(qb_map_t * m, const char *key,
+		    qb_map_notify_fn fn, int32_t events, void *user_data)
+{
+	struct skiplist *t = (struct skiplist *)m;
+	struct qb_map_notifier *f;
+	struct skiplist_node *n;
+	struct qb_list_head *list;
+	int add_to_tail = QB_FALSE;
+
+	if (key) {
+		n = skiplist_lookup(t, key);
+	} else {
+		n = t->header;
+	}
+	if (events & QB_MAP_NOTIFY_FREE) {
+		add_to_tail = QB_TRUE;
+	}
+	if (n) {
+		qb_list_for_each(list, &n->notifier_head) {
+			f = qb_list_entry(list, struct qb_map_notifier, list);
+
+			if (events & QB_MAP_NOTIFY_FREE &&
+			    f->events == events) {
+				/* only one free notifier */
+				return -EEXIST;
+			}
+			if (f->events == events &&
+			    f->callback == fn &&
+			    f->user_data == user_data) {
+				return -EEXIST;
+			}
+		}
+
+		f = malloc(sizeof(struct qb_map_notifier));
+		if (f == NULL) {
+			return -errno;
+		}
+		f->events = events;
+		f->user_data = user_data;
+		f->callback = fn;
+		qb_list_init(&f->list);
+		if (add_to_tail) {
+			qb_list_add_tail(&f->list, &n->notifier_head);
+		} else {
+			qb_list_add(&f->list, &n->notifier_head);
+		}
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int32_t
+skiplist_notify_del(qb_map_t * m, const char *key,
+		    qb_map_notify_fn fn, int32_t events,
+		    int32_t cmp_userdata, void *user_data)
+{
+	struct skiplist *t = (struct skiplist *)m;
+	struct skiplist_node *n;
+	struct qb_map_notifier *f;
+	struct qb_list_head *head = NULL;
+	struct qb_list_head *list;
+	struct qb_list_head *next;
+	int32_t found = QB_FALSE;
+
+	if (key) {
+		n = skiplist_lookup(t, key);
+		if (n) {
+			head = &n->notifier_head;
+		}
+	} else {
+		head = &t->header->notifier_head;
+	}
+	if (head == NULL) {
+		return -ENOENT;
+	}
+	qb_list_for_each_safe(list, next, head) {
+		f = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (f->events == events && f->callback == fn) {
+			if (cmp_userdata && (f->user_data == user_data)) {
+				found = QB_TRUE;
+				qb_list_del(&f->list);
+				free(f);
+			} else if (!cmp_userdata) {
+				found = QB_TRUE;
+				qb_list_del(&f->list);
+				free(f);
+			}
+		}
+	}
+	if (found) {
+		return 0;
+	} else {
+		return -ENOENT;
+	}
+}
+
+static void
+skiplist_destroy(struct qb_map *map)
+{
+	struct skiplist *list = (struct skiplist *)map;
+	struct skiplist_node *cur_node;
+	struct skiplist_node *fwd_node;
+
+	for (cur_node = skiplist_node_next(list->header);
+	     cur_node; cur_node = fwd_node) {
+		fwd_node = skiplist_node_next(cur_node);
+		skiplist_node_destroy(cur_node, list);
+	}
+	skiplist_node_destroy(list->header, list);
+	free(list);
+}
+
+static void
+skiplist_put(struct qb_map *map, const char *key, const void *value)
+{
+	struct skiplist *list = (struct skiplist *)map;
+	struct skiplist_node *new_node;
+	int8_t level = list->level;
+	skiplist_update_t update;
+	int8_t update_level;
+	int8_t new_node_level;
+	struct skiplist_node *cur_node = list->header;
+	char *old_k;
+	char *old_v;
+
+	while ((update_level = level) >= SKIPLIST_LEVEL_MIN) {
+		struct skiplist_node *fwd_node = cur_node->forward[level];
+
+		switch (op_search(list, fwd_node, key)) {
+		case OP_FINISH:
+			old_k = (char *)fwd_node->key;
+			old_v = (char *)fwd_node->value;
+			fwd_node->value = (void *)value;
+			fwd_node->key = (void *)key;
+			skiplist_notify(list, fwd_node,
+					QB_MAP_NOTIFY_REPLACED,
+					old_k, old_v, fwd_node->value);
+			return;
+
+		case OP_GOTO_NEXT_NODE:
+			cur_node = fwd_node;
+			break;
+		case OP_GOTO_NEXT_LEVEL:
+			level--;
+		}
+
+		update[update_level] = cur_node;
+	}
+
+	new_node_level = skiplist_level_generate();
+
+	if (new_node_level > list->level) {
+		for (level = list->level + 1; level <= new_node_level; level++)
+			update[level] = list->header;
+
+		list->level = new_node_level;
+	}
+
+	new_node = skiplist_node_new(new_node_level, key, value);
+
+	assert(new_node != NULL);
+	skiplist_notify(list, new_node,
+			QB_MAP_NOTIFY_INSERTED,
+			(char*)new_node->key, NULL, new_node->value);
+
+	/* Drop @new_node into @list. */
+	for (level = SKIPLIST_LEVEL_MIN; level <= new_node_level; level++) {
+		new_node->forward[level] = update[level]->forward[level];
+		update[level]->forward[level] = new_node;
+	}
+
+	list->length++;
+}
+
+static int32_t
+skiplist_rm(struct qb_map *map, const char *key)
+{
+	struct skiplist *list = (struct skiplist *)map;
+	struct skiplist_node *found_node;
+	struct skiplist_node *cur_node = list->header;
+	int8_t level = list->level;
+	int8_t update_level;
+	skiplist_update_t update;
+
+	while ((update_level = level) >= SKIPLIST_LEVEL_MIN) {
+		struct skiplist_node *fwd_node = cur_node->forward[level];
+
+		switch (op_search(list, fwd_node, key)) {
+		case OP_GOTO_NEXT_NODE:
+			cur_node = fwd_node;
+			break;
+		case OP_GOTO_NEXT_LEVEL:
+		default:
+			level--;
+			break;
+		}
+
+		update[update_level] = cur_node;
+	}
+
+	/* The immediate forward node should be the matching node... */
+	found_node = skiplist_node_next(cur_node);
+
+	/* ...unless we're at the end of the list or the value doesn't exist. */
+	if (!found_node || strcmp(found_node->key, key) != 0) {
+		return QB_FALSE;
+	}
+
+	/* Splice found_node out of list. */
+	for (level = SKIPLIST_LEVEL_MIN; level <= list->level; level++)
+		if (update[level]->forward[level] == found_node)
+			update[level]->forward[level] =
+			    found_node->forward[level];
+
+	skiplist_node_deref(found_node, list);
+
+	/* Remove unused levels from @list -- stop removing levels as soon as a
+	 * used level is found. Unused levels can occur if @found_node had the
+	 * highest level.
+	 */
+	for (level = list->level; level >= SKIPLIST_LEVEL_MIN; level--) {
+		if (list->header->forward[level])
+			break;
+
+		list->level--;
+	}
+
+	list->length--;
+
+	return QB_TRUE;
+}
+
+static void *
+skiplist_get(struct qb_map *map, const char *key)
+{
+	struct skiplist *list = (struct skiplist *)map;
+	struct skiplist_node *n = skiplist_lookup(list, key);
+	if (n) {
+		return n->value;
+	}
+
+	return NULL;
+}
+
+static qb_map_iter_t *
+skiplist_iter_create(struct qb_map *map, const char *prefix)
+{
+	struct skiplist_iter *i = malloc(sizeof(struct skiplist_iter));
+	struct skiplist *list = (struct skiplist *)map;
+	if (i == NULL) {
+		return NULL;
+	}
+	i->i.m = map;
+	i->n = list->header;
+	i->n->refcount++;
+	return (qb_map_iter_t *) i;
+}
+
+static const char *
+skiplist_iter_next(qb_map_iter_t * i, void **value)
+{
+	struct skiplist_iter *si = (struct skiplist_iter *)i;
+	struct skiplist_node *p = si->n;
+
+	if (p == NULL) {
+		return NULL;
+	}
+	si->n = skiplist_node_next(p);
+	if (si->n == NULL) {
+		skiplist_node_deref(p, (struct skiplist *)i->m);
+		return NULL;
+	}
+	si->n->refcount++;
+	skiplist_node_deref(p, (struct skiplist *)i->m);
+	*value = si->n->value;
+	return si->n->key;
+}
+
+static void
+skiplist_iter_free(qb_map_iter_t * i)
+{
+	free(i);
+}
+
+static size_t
+skiplist_count_get(struct qb_map *map)
+{
+	struct skiplist *list = (struct skiplist *)map;
+	return list->length;
+}
+
+qb_map_t *
+qb_skiplist_create(void)
+{
+	struct skiplist *sl = malloc(sizeof(struct skiplist));
+	if (sl == NULL) {
+		return NULL;
+	}
+
+	srand(time(NULL));
+
+	sl->map.put = skiplist_put;
+	sl->map.get = skiplist_get;
+	sl->map.rm = skiplist_rm;
+	sl->map.count_get = skiplist_count_get;
+	sl->map.iter_create = skiplist_iter_create;
+	sl->map.iter_next = skiplist_iter_next;
+	sl->map.iter_free = skiplist_iter_free;
+	sl->map.destroy = skiplist_destroy;
+	sl->map.notify_add = skiplist_notify_add;
+	sl->map.notify_del = skiplist_notify_del;
+	sl->level = SKIPLIST_LEVEL_MIN;
+	sl->length = 0;
+	sl->header = skiplist_header_node_new();
+
+	return (qb_map_t *) sl;
+}
diff --git a/lib/strchrnul.c b/lib/strchrnul.c
new file mode 100644
index 0000000..e404650
--- /dev/null
+++ b/lib/strchrnul.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+
+/* Find the first occurrence of C in S or the final NUL byte.
+ */
+char *
+strchrnul(const char *s, int c_in)
+{
+	char c = c_in;
+	while (*s && (*s != c))
+		s++;
+
+	return (char *) s;
+}
+
diff --git a/lib/strlcat.c b/lib/strlcat.c
new file mode 100644
index 0000000..71412a0
--- /dev/null
+++ b/lib/strlcat.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 Alan Robertson <alanr at unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include "os_base.h"
+
+/*
+ * strlcat() appends string src to the end of dst.  It will append at most
+ * maxlen - strlen(dst) - 1 characters.  It will then NUL-terminate, unless
+ * maxlen is 0 or the original dst string was longer than maxlen (in
+ * practice this should not happen as it means that either maxlen is
+ * incorrect or that dst is not a proper string).
+ *
+ * @return the total length of the string it tried to create
+ *         (the length of dst plus the length of src).
+ */
+size_t
+strlcat(char *dest, const char * src, size_t maxlen)
+{
+	size_t	curlen = strlen(dest);
+	size_t	addlen = strlen(src);
+	size_t	appendlen = maxlen - curlen;
+	if (appendlen > 0) {
+		strlcpy(dest+curlen, src, appendlen);
+	}
+	return curlen + addlen;
+}
diff --git a/lib/strlcpy.c b/lib/strlcpy.c
new file mode 100644
index 0000000..4071edf
--- /dev/null
+++ b/lib/strlcpy.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 Alan Robertson <alanr at unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include "os_base.h"
+#include <qb/qbdefs.h>
+
+/*
+ * strlcpy() copies up to maxlen - 1 characters from the string src to dst,
+ * NUL-terminating the result if maxlen is not 0.
+ *
+ * @return the total length of the string it tried to create
+ *         (the length of src).
+ */
+size_t
+strlcpy(char *dest, const char * src, size_t maxlen)
+{
+	size_t	srclen = strlen(src);
+	size_t	len2cpy = QB_MIN(maxlen-1, srclen);
+
+	if (len2cpy > 0) {
+		strncpy(dest, src, len2cpy+1);
+		dest[len2cpy] = '\0';
+	}
+	return srclen;
+}
diff --git a/lib/trie.c b/lib/trie.c
new file mode 100644
index 0000000..43bed90
--- /dev/null
+++ b/lib/trie.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <os_base.h>
+#include <assert.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblist.h>
+#include <qb/qbmap.h>
+#include "map_int.h"
+
+struct trie_iter {
+	struct qb_map_iter i;
+	const char *prefix;
+	struct trie_node *n;
+	struct trie_node *root;
+};
+
+struct trie_node {
+	uint32_t idx;
+	char *segment;
+	uint32_t num_segments;
+	char *key;
+	void *value;
+	struct trie_node **children;
+	uint32_t num_children;
+	uint32_t refcount;
+	struct trie_node *parent;
+	struct qb_list_head *notifier_head;
+};
+
+struct trie {
+	struct qb_map map;
+
+	size_t length;
+	uint32_t num_nodes;
+	uint32_t mem_used;
+	struct trie_node *header;
+};
+
+static void trie_notify(struct trie_node *n, uint32_t event, const char *key,
+			void *old_value, void *value);
+static struct trie_node *trie_new_node(struct trie *t, struct trie_node *parent);
+static void trie_destroy_node(struct trie_node *node);
+
+/*
+ * characters are stored in reverse to make accessing the
+ * more common case (non-control chars) more space efficient.
+ */
+#define TRIE_CHAR2INDEX(ch) (126 - ch)
+#define TRIE_INDEX2CHAR(idx) (126 - idx)
+
+
+static int32_t
+trie_node_alive(struct trie_node *node)
+{
+	if (node->value == NULL ||
+	    node->refcount <= 0) {
+		return QB_FALSE;
+	}
+	return QB_TRUE;
+}
+
+static struct trie_node *
+trie_node_next(struct trie_node *node, struct trie_node *root, int all)
+{
+	struct trie_node *c = node;
+	struct trie_node *n;
+	struct trie_node *p;
+	int i;
+
+keep_going:
+	n = NULL;
+
+	/* child/outward
+	 */
+	for (i = c->num_children - 1; i >= 0; i--) {
+		if (c->children[i]) {
+			n = c->children[i];
+			break;
+		}
+	}
+	if (n) {
+		if (all || trie_node_alive(n)) {
+			return n;
+		} else {
+			c = n;
+			goto keep_going;
+		}
+	}
+	/* sibling/parent
+	 */
+	if (c == root) {
+		return NULL;
+	}
+	p = c;
+	do {
+		for (i = p->idx - 1; i >= 0; i--) {
+			if (p->parent->children[i]) {
+				n = p->parent->children[i];
+				break;
+			}
+		}
+		if (n == NULL) {
+			p = p->parent;
+		}
+	} while (n == NULL && p != root);
+
+	if (n) {
+		if (all || trie_node_alive(n)) {
+			return n;
+		}
+		if (n == root) {
+			return NULL;
+		}
+		c = n;
+		goto keep_going;
+	}
+
+	return n;
+}
+
+static struct trie_node *
+new_child_node(struct trie *t, struct trie_node * parent, char ch)
+{
+	struct trie_node *new_node;
+	int old_max_idx;
+	int i;
+	int idx = TRIE_CHAR2INDEX(ch);
+
+	if (idx >= parent->num_children) {
+		old_max_idx = parent->num_children;
+		parent->num_children = QB_MAX(idx + 1, 30);
+		t->mem_used += (sizeof(struct trie_node*) * (parent->num_children - old_max_idx));
+		parent->children = realloc(parent->children,
+				(parent->num_children * sizeof(struct trie_node*)));
+		if (parent->children == NULL) {
+			return NULL;
+		}
+		for (i = old_max_idx; i < parent->num_children; i++) {
+			parent->children[i] = NULL;
+		}
+	}
+	new_node = trie_new_node(t, parent);
+	if (new_node == NULL) {
+		return NULL;
+	}
+	new_node->idx = idx;
+	parent->children[idx] = new_node;
+	return new_node;
+}
+
+
+static struct trie_node *
+trie_node_split(struct trie *t, struct trie_node *cur_node, int seg_cnt)
+{
+	struct trie_node *split_node;
+	struct trie_node ** children = cur_node->children;
+	uint32_t num_children = cur_node->num_children;
+	struct qb_list_head *tmp;
+	int i;
+	int s;
+
+	cur_node->children = NULL;
+	cur_node->num_children = 0;
+	split_node = new_child_node(t, cur_node, cur_node->segment[seg_cnt]);
+	if (split_node == NULL) {
+		return NULL;
+	}
+	split_node->children = children;
+	split_node->num_children = num_children;
+	for (i = 0; i < split_node->num_children; i++) {
+		if (split_node->children[i]) {
+			split_node->children[i]->parent = split_node;
+		}
+	}
+	split_node->value = cur_node->value;
+	split_node->key = cur_node->key;
+	split_node->refcount = cur_node->refcount;
+	cur_node->value = NULL;
+	cur_node->key = NULL;
+	cur_node->refcount = 0;
+	/* move notifier list to split */
+	tmp = split_node->notifier_head;
+	split_node->notifier_head = cur_node->notifier_head;
+	cur_node->notifier_head = tmp;
+	qb_list_init(cur_node->notifier_head);
+
+	if (seg_cnt < cur_node->num_segments) {
+		split_node->num_segments = cur_node->num_segments - seg_cnt - 1;
+		split_node->segment = malloc(split_node->num_segments * sizeof(char));
+		if (split_node->segment == NULL) {
+			trie_destroy_node(split_node);
+			return NULL;
+		}
+		for (i = (seg_cnt + 1); i < cur_node->num_segments; i++) {
+			s = i - seg_cnt - 1;
+			split_node->segment[s] = cur_node->segment[i];
+			cur_node->segment[i] = '\0';
+		}
+		cur_node->num_segments = seg_cnt;
+	}
+	return cur_node;
+}
+
+static struct trie_node *
+trie_insert(struct trie *t, const char *key)
+{
+	struct trie_node *cur_node = t->header;
+	struct trie_node *new_node;
+	char *cur = (char *)key;
+	int idx = TRIE_CHAR2INDEX(key[0]);
+	int seg_cnt = 0;
+
+	do {
+		new_node = NULL;
+		if (cur_node->num_segments > 0 &&
+		    seg_cnt < cur_node->num_segments) {
+			if (cur_node->segment[seg_cnt] == *cur) {
+				/* we found the char in the segment */
+				seg_cnt++;
+			} else {
+				cur_node = trie_node_split(t, cur_node, seg_cnt);
+				if (cur_node == NULL) {
+					return NULL;
+				}
+				new_node = new_child_node(t, cur_node, *cur);
+				if (new_node == NULL) {
+					return NULL;
+				}
+			}
+		} else if (idx < cur_node->num_children &&
+		    cur_node->children[idx]) {
+			/* the char can be found on the next node */
+			new_node = cur_node->children[idx];
+		} else if (cur_node == t->header) {
+			/* the root node is empty so make it on the next node */
+			new_node = new_child_node(t, cur_node, *cur);
+			if (new_node == NULL) {
+				return NULL;
+			}
+		} else if (cur_node->value == NULL &&
+			   qb_list_empty(cur_node->notifier_head) &&
+			   cur_node->num_children == 0 &&
+			   seg_cnt == cur_node->num_segments) {
+			/* we are on a leaf (with no value) so just add it as a segment */
+			cur_node->segment = realloc(cur_node->segment, cur_node->num_segments + 1);
+			cur_node->segment[cur_node->num_segments] = *cur;
+			t->mem_used += sizeof(char);
+			cur_node->num_segments++;
+			seg_cnt++;
+		} else if (seg_cnt == cur_node->num_segments) {
+			/* on the last segment need to make a new node */
+			new_node = new_child_node(t, cur_node, *cur);
+			if (new_node == NULL) {
+				return NULL;
+			}
+		} else /* need_to_split */ {
+			cur_node = trie_node_split(t, cur_node, seg_cnt);
+			if (cur_node == NULL) {
+				return NULL;
+			}
+			new_node = new_child_node(t, cur_node, *cur);
+			if (new_node == NULL) {
+				return NULL;
+			}
+		}
+		if (new_node) {
+			seg_cnt = 0;
+			cur_node = new_node;
+		}
+		cur++;
+		idx = TRIE_CHAR2INDEX(*cur);
+	} while (*cur != '\0');
+
+	if (cur_node->num_segments > 0 &&
+	    seg_cnt < cur_node->num_segments) {
+		/* we need to split */
+		cur_node = trie_node_split(t, cur_node, seg_cnt);
+		if (cur_node == NULL) {
+			return NULL;
+		}
+		new_node = new_child_node(t, cur_node, *cur);
+		if (new_node == NULL) {
+			return NULL;
+		}
+	}
+
+	return cur_node;
+}
+
+static struct trie_node *
+trie_lookup(struct trie *t, const char *key, int exact_match)
+{
+	struct trie_node *cur_node = t->header;
+	char *cur = (char *)key;
+	int idx = TRIE_CHAR2INDEX(key[0]);
+	int seg_cnt = 0;
+
+	do {
+		if (cur_node->num_segments > 0 &&
+		    seg_cnt < cur_node->num_segments) {
+			if (cur_node->segment[seg_cnt] == *cur) {
+				/* we found the char in the segment */
+				seg_cnt++;
+			} else {
+				return NULL;
+			}
+		} else if (idx < cur_node->num_children &&
+		    cur_node->children[idx]) {
+			/* the char can be found on the next node */
+			cur_node = cur_node->children[idx];
+			seg_cnt = 0;
+		} else {
+			return NULL;
+		}
+		cur++;
+		idx = TRIE_CHAR2INDEX(*cur);
+	} while (*cur != '\0');
+
+	if (exact_match &&
+	    cur_node->num_segments > 0 &&
+	    seg_cnt < cur_node->num_segments) {
+		return NULL;
+	}
+
+	return cur_node;
+}
+
+static void
+trie_node_release(struct trie *t, struct trie_node *node)
+{
+	int i;
+	int empty = QB_FALSE;
+
+	if (node->key == NULL &&
+	    node->parent != NULL &&
+	    qb_list_empty(node->notifier_head)) {
+		struct trie_node *p = node->parent;
+
+		if (node->num_children == 0) {
+			empty = QB_TRUE;
+		} else {
+			empty = QB_TRUE;
+			for (i = node->num_children - 1; i >= 0; i--) {
+				if (node->children[i]) {
+					empty = QB_FALSE;
+					break;
+				}
+			}
+		}
+		if (!empty) {
+			return;
+		}
+
+		/*
+		 * unlink the node from the parent
+		 */
+		p->children[node->idx] = NULL;
+		trie_destroy_node(node);
+		t->num_nodes--;
+		t->mem_used -= sizeof(struct trie_node);
+
+		trie_node_release(t, p);
+	}
+}
+
+static void
+trie_node_destroy(struct trie *t, struct trie_node *n)
+{
+	if (n->value == NULL) {
+		return;
+	}
+	trie_notify(n, QB_MAP_NOTIFY_DELETED, n->key, n->value, NULL);
+
+	n->key = NULL;
+	n->value = NULL;
+
+	trie_node_release(t, n);
+}
+
+static void
+trie_print_node(struct trie_node *n, struct trie_node *r, const char *suffix)
+{
+	int i;
+
+	if (n->parent) {
+		trie_print_node(n->parent, n, suffix);
+	}
+	if (n->idx == 0) {
+		return;
+	}
+
+	printf("[%c", TRIE_INDEX2CHAR(n->idx));
+	for (i = 0; i < n->num_segments; i++) {
+		printf("%c", n->segment[i]);
+	}
+	if (n == r) {
+		printf("] (%d) %s\n", n->refcount, suffix);
+	} else {
+		printf("] ");
+	}
+}
+
+static void
+trie_node_ref(struct trie *t, struct trie_node *node)
+{
+	if (t->header == node) {
+		return;
+	}
+	node->refcount++;
+}
+
+static void
+trie_node_deref(struct trie *t, struct trie_node *node)
+{
+	if (!trie_node_alive(node)) {
+		return;
+	}
+	node->refcount--;
+	if (node->refcount > 0) {
+		return;
+	}
+	trie_node_destroy(t, node);
+}
+
+static void
+trie_destroy(struct qb_map *map)
+{
+	struct trie *t = (struct trie *)map;
+
+	struct trie_node *cur_node = t->header;
+	struct trie_node *fwd_node;
+
+	do {
+		fwd_node = trie_node_next(cur_node, t->header, QB_FALSE);
+		trie_node_destroy(t, cur_node);
+	} while ((cur_node = fwd_node));
+
+	free(t);
+}
+
+static void
+trie_destroy_node(struct trie_node *node)
+{
+	free(node->segment);
+	free(node->children);
+	free(node->notifier_head);
+	free(node);
+}
+
+static struct trie_node *
+trie_new_node(struct trie *t, struct trie_node *parent)
+{
+	struct trie_node *new_node = calloc(1, sizeof(struct trie_node));
+
+	if (new_node == NULL) {
+		return NULL;
+	}
+
+	new_node->notifier_head = calloc(1, sizeof(struct qb_list_head));
+	if (new_node->notifier_head == NULL) {
+		free(new_node);
+		return NULL;
+	}
+
+	new_node->parent = parent;
+	new_node->num_children = 0;
+	new_node->children = NULL;
+	new_node->num_segments = 0;
+	new_node->segment = NULL;
+	t->num_nodes++;
+	t->mem_used += sizeof(struct trie_node);
+	qb_list_init(new_node->notifier_head);
+	return new_node;
+}
+
+void
+qb_trie_dump(qb_map_t* m)
+{
+	struct trie * t = (struct trie*)m;
+	struct trie_node *n;
+
+	if (t == NULL) {
+		return;
+	}
+
+	printf("nodes: %d, bytes: %d\n", t->num_nodes, t->mem_used);
+
+	n = t->header;
+	do {
+		if (n->num_children == 0) {
+			trie_print_node(n, n, " ");
+		}
+		n = trie_node_next(n, t->header, QB_FALSE);
+	} while (n);
+}
+
+static void
+trie_put(struct qb_map *map, const char *key, const void *value)
+{
+	struct trie *t = (struct trie *)map;
+	struct trie_node *n = trie_insert(t, key);
+	if (n) {
+		const char *old_value = n->value;
+		const char *old_key = n->key;
+
+		n->key = (char *)key;
+		n->value = (void *)value;
+
+		if (old_value == NULL) {
+			trie_node_ref(t, n);
+			t->length++;
+			trie_notify(n, QB_MAP_NOTIFY_INSERTED,
+				    n->key, NULL, n->value);
+		} else {
+			trie_notify(n, QB_MAP_NOTIFY_REPLACED,
+				    (char *)old_key, (void *)old_value,
+				    (void *)value);
+		}
+	}
+}
+
+static int32_t
+trie_rm(struct qb_map *map, const char *key)
+{
+	struct trie *t = (struct trie *)map;
+	struct trie_node *n = trie_lookup(t, key, QB_TRUE);
+	if (n) {
+		trie_node_deref(t, n);
+		t->length--;
+		return QB_TRUE;
+	} else {
+		return QB_FALSE;
+	}
+}
+
+static void *
+trie_get(struct qb_map *map, const char *key)
+{
+	struct trie *t = (struct trie *)map;
+	struct trie_node *n = trie_lookup(t, key, QB_TRUE);
+	if (n) {
+		return n->value;
+	}
+
+	return NULL;
+}
+
+static void
+trie_notify_deref(struct qb_map_notifier *f)
+{
+	f->refcount--;
+	if (f->refcount == 0) {
+		qb_list_del(&f->list);
+		free(f);
+	}
+}
+
+static void
+trie_notify_ref(struct qb_map_notifier *f)
+{
+	f->refcount++;
+}
+
+static void
+trie_notify(struct trie_node *n,
+	    uint32_t event, const char *key, void *old_value, void *value)
+{
+	struct trie_node *c = n;
+	struct qb_list_head *list;
+	struct qb_list_head *next;
+	struct qb_list_head *head;
+	struct qb_map_notifier *tn;
+
+	do {
+		head = c->notifier_head;
+		qb_list_for_each_safe(list, next, head) {
+			tn = qb_list_entry(list, struct qb_map_notifier, list);
+			trie_notify_ref(tn);
+
+			if ((tn->events & event) &&
+			    ((tn->events & QB_MAP_NOTIFY_RECURSIVE) ||
+			     (n == c))) {
+				tn->callback(event, (char *)key, old_value,
+					     value, tn->user_data);
+			}
+			if (((event & QB_MAP_NOTIFY_DELETED) ||
+			     (event & QB_MAP_NOTIFY_REPLACED)) &&
+			    (tn->events & QB_MAP_NOTIFY_FREE)) {
+				tn->callback(QB_MAP_NOTIFY_FREE, (char *)key,
+					     old_value, value, tn->user_data);
+			}
+
+			trie_notify_deref(tn);
+		}
+		c = c->parent;
+	} while (c);
+}
+
+static int32_t
+trie_notify_add(qb_map_t * m, const char *key,
+		qb_map_notify_fn fn, int32_t events, void *user_data)
+{
+	struct trie *t = (struct trie *)m;
+	struct qb_map_notifier *f;
+	struct trie_node *n;
+	struct qb_list_head *list;
+	int add_to_tail = QB_FALSE;
+
+	if (key) {
+		n = trie_lookup(t, key, QB_TRUE);
+		if (n == NULL) {
+			n = trie_insert(t, key);
+		}
+	} else {
+		n = t->header;
+	}
+	if (n) {
+		qb_list_for_each(list, n->notifier_head) {
+			f = qb_list_entry(list, struct qb_map_notifier, list);
+
+			if (events & QB_MAP_NOTIFY_FREE &&
+			    f->events == events) {
+				/* only one free notifier */
+				return -EEXIST;
+			}
+			if (f->events == events &&
+			    f->callback == fn &&
+			    f->user_data == user_data) {
+				return -EEXIST;
+			}
+		}
+
+		f = malloc(sizeof(struct qb_map_notifier));
+		if (f == NULL) {
+			return -errno;
+		}
+		f->events = events;
+		f->user_data = user_data;
+		f->callback = fn;
+		f->refcount = 1;
+		qb_list_init(&f->list);
+		if (key) {
+			if (events & QB_MAP_NOTIFY_RECURSIVE) {
+				add_to_tail = QB_TRUE;
+			}
+		} else {
+			if (events & QB_MAP_NOTIFY_FREE) {
+				add_to_tail = QB_TRUE;
+			}
+		}
+		if (add_to_tail) {
+			qb_list_add_tail(&f->list, n->notifier_head);
+		} else {
+			qb_list_add(&f->list, n->notifier_head);
+		}
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int32_t
+trie_notify_del(qb_map_t * m, const char *key,
+		qb_map_notify_fn fn, int32_t events,
+		int32_t cmp_userdata, void *user_data)
+{
+	struct trie *t = (struct trie *)m;
+	struct trie_node *n;
+	struct qb_list_head *list;
+	struct qb_list_head *next;
+	int32_t found = QB_FALSE;
+
+	if (key) {
+		n = trie_lookup(t, key, QB_FALSE);
+	} else {
+		n = t->header;
+	}
+	if (n == NULL) {
+		return -ENOENT;
+	}
+	qb_list_for_each_safe(list, next, n->notifier_head) {
+		struct qb_map_notifier *f = qb_list_entry(list, struct qb_map_notifier, list);
+
+		if (f->events == events && f->callback == fn) {
+			if (cmp_userdata && (f->user_data == user_data)) {
+				trie_notify_deref(f);
+				found = QB_TRUE;
+			} else if (!cmp_userdata) {
+				trie_notify_deref(f);
+				found = QB_TRUE;
+			}
+		}
+
+	}
+	if (found) {
+		trie_node_release(t, n);
+		return 0;
+	} else {
+		return -ENOENT;
+	}
+}
+
+static qb_map_iter_t *
+trie_iter_create(struct qb_map *map, const char *prefix)
+{
+	struct trie_iter *i = malloc(sizeof(struct trie_iter));
+	struct trie *t = (struct trie *)map;
+	if (i == NULL) {
+		return NULL;
+	}
+	i->i.m = map;
+	i->prefix = prefix;
+	i->n = t->header;
+	i->root = t->header;
+	return (qb_map_iter_t *) i;
+}
+
+static const char *
+trie_iter_next(qb_map_iter_t * i, void **value)
+{
+	struct trie_iter *si = (struct trie_iter *)i;
+	struct trie_node *p = si->n;
+	struct trie *t = (struct trie *)(i->m);
+
+	if (p == NULL) {
+		return NULL;
+	}
+
+	if (p->parent == NULL && si->prefix) {
+		si->root = trie_lookup(t, si->prefix, QB_FALSE);
+		if (si->root == NULL) {
+			si->n = NULL;
+		} else if (si->root->value == NULL) {
+			si->n = trie_node_next(si->root, si->root, QB_FALSE);
+		} else {
+			si->n = si->root;
+		}
+	} else {
+		si->n = trie_node_next(p, si->root, QB_FALSE);
+	}
+	if (si->n == NULL) {
+		trie_node_deref(t, p);
+		return NULL;
+	}
+	trie_node_ref(t, si->n);
+	trie_node_deref(t, p);
+	*value = si->n->value;
+	return si->n->key;
+}
+
+static void
+trie_iter_free(qb_map_iter_t * i)
+{
+	struct trie_iter *si = (struct trie_iter *)i;
+	struct trie *t = (struct trie *)(i->m);
+
+	if (si->n != NULL) {
+		/* if free'ing the iterator before getting to the last
+		 * node make sure we de-ref the current node.
+		 */
+		trie_node_deref(t, si->n);
+	}
+	free(i);
+}
+
+static size_t
+trie_count_get(struct qb_map *map)
+{
+	struct trie *list = (struct trie *)map;
+	return list->length;
+}
+
+qb_map_t *
+qb_trie_create(void)
+{
+	struct trie *t = malloc(sizeof(struct trie));
+	if (t == NULL) {
+		return NULL;
+	}
+
+	t->map.put = trie_put;
+	t->map.get = trie_get;
+	t->map.rm = trie_rm;
+	t->map.count_get = trie_count_get;
+	t->map.iter_create = trie_iter_create;
+	t->map.iter_next = trie_iter_next;
+	t->map.iter_free = trie_iter_free;
+	t->map.destroy = trie_destroy;
+	t->map.notify_add = trie_notify_add;
+	t->map.notify_del = trie_notify_del;
+	t->length = 0;
+	t->num_nodes = 0;
+	t->mem_used = sizeof(struct trie);
+	t->header = trie_new_node(t, NULL);
+
+	return (qb_map_t *) t;
+}
diff --git a/lib/unix.c b/lib/unix.c
new file mode 100644
index 0000000..79ed538
--- /dev/null
+++ b/lib/unix.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#ifdef HAVE_SYS_SHM_H
+#include <sys/shm.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include "util_int.h"
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qbatomic.h>
+
+#if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS)
+/*
+ * BSD derivatives usually have MAP_ANON, not MAP_ANONYMOUS
+ **/
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+
+char *
+qb_strerror_r(int errnum, char *buf, size_t buflen)
+{
+#ifdef STRERROR_R_CHAR_P
+	return strerror_r(errnum, buf, buflen);
+#else
+	if (strerror_r(errnum, buf, buflen) != 0) {
+		buf[0] = '\0';
+	}
+	return buf;
+#endif /* STRERROR_R_CHAR_P */
+}
+
+static int32_t
+open_mmap_file(char *path, uint32_t file_flags)
+{
+	if (strstr(path, "XXXXXX") != NULL) {
+		mode_t old_mode = umask(077);
+		int32_t temp_fd = mkstemp(path);
+		(void)umask(old_mode);
+		return temp_fd;
+	}
+
+	return open(path, file_flags, 0600);
+}
+
+int32_t
+qb_sys_mmap_file_open(char *path, const char *file, size_t bytes,
+		       uint32_t file_flags)
+{
+	int32_t fd;
+	int32_t i;
+	int32_t res = 0;
+	ssize_t written;
+	char *buffer = NULL;
+	char *is_absolute = strchr(file, '/');
+
+	if (is_absolute) {
+		(void)strlcpy(path, file, PATH_MAX);
+	} else {
+#if defined(QB_LINUX) || defined(QB_CYGWIN)
+		snprintf(path, PATH_MAX, "/dev/shm/%s", file);
+#else
+		snprintf(path, PATH_MAX, LOCALSTATEDIR "/run/%s", file);
+		is_absolute = path;
+#endif
+	}
+	fd = open_mmap_file(path, file_flags);
+	if (fd < 0 && !is_absolute) {
+		qb_util_perror(LOG_ERR, "couldn't open file %s", path);
+
+		snprintf(path, PATH_MAX, LOCALSTATEDIR "/run/%s", file);
+		fd = open_mmap_file(path, file_flags);
+		if (fd < 0) {
+			res = -errno;
+			qb_util_perror(LOG_ERR, "couldn't open file %s", path);
+			return res;
+		}
+	} else if (fd < 0 && is_absolute) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "couldn't open file %s", path);
+		return res;
+	}
+
+	if (ftruncate(fd, bytes) == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR, "couldn't truncate file %s", path);
+		goto unlink_exit;
+	}
+
+	if (file_flags & O_CREAT) {
+		long page_size = sysconf(_SC_PAGESIZE);
+		long write_size = QB_MIN(page_size, bytes);
+		if (page_size < 0) {
+			res = -errno;
+			goto unlink_exit;
+		}
+		buffer = calloc(1, write_size);
+		if (buffer == NULL) {
+			res = -ENOMEM;
+			goto unlink_exit;
+		}
+		for (i = 0; i < (bytes / write_size); i++) {
+retry_write:
+			written = write(fd, buffer, write_size);
+			if (written == -1 && errno == EINTR) {
+				goto retry_write;
+			}
+			if (written != write_size) {
+				res = -ENOSPC;
+				free(buffer);
+				goto unlink_exit;
+			}
+		}
+		free(buffer);
+	}
+
+	return fd;
+
+unlink_exit:
+	unlink(path);
+	if (fd >= 0) {
+		close(fd);
+	}
+	return res;
+}
+
+
+int32_t
+qb_sys_circular_mmap(int32_t fd, void **buf, size_t bytes)
+{
+	void *addr_orig = NULL;
+	void *addr;
+	void *addr_next;
+	int32_t res;
+	int flags = MAP_ANONYMOUS;
+
+#ifdef QB_FORCE_SHM_ALIGN
+/* On a number of arches any fixed and shared mmap() mapping address
+ * must be aligned to 16k. If the first mmap() below is not shared then
+ * the first mmap() will succeed because these restrictions do not apply to
+ * private mappings. The second mmap() wants a shared memory mapping but
+ * the address returned by the first one is only page-aligned and not
+ * aligned to 16k.
+ */
+	flags |= MAP_SHARED;
+#else
+	flags |= MAP_PRIVATE;
+#endif /* QB_FORCE_SHM_ALIGN */
+
+	addr_orig = mmap(NULL, bytes << 1, PROT_NONE, flags, -1, 0);
+
+	if (addr_orig == MAP_FAILED) {
+		return -errno;
+	}
+
+	addr = mmap(addr_orig, bytes, PROT_READ | PROT_WRITE,
+		    MAP_FIXED | MAP_SHARED, fd, 0);
+
+	if (addr != addr_orig) {
+		res = -errno;
+		goto cleanup_fail;
+	}
+#if defined(QB_BSD) && defined(MADV_NOSYNC)
+	madvise(addr_orig, bytes, MADV_NOSYNC);
+#endif
+	addr_next = ((char *)addr_orig) + bytes;
+	addr = mmap(addr_next,
+		    bytes, PROT_READ | PROT_WRITE,
+		    MAP_FIXED | MAP_SHARED, fd, 0);
+	if (addr != addr_next) {
+		res = -errno;
+		goto cleanup_fail;
+	}
+#if defined(QB_BSD) && defined(MADV_NOSYNC)
+	madvise(((char *)addr_orig) + bytes, bytes, MADV_NOSYNC);
+#endif
+
+	res = close(fd);
+	if (res) {
+		goto cleanup_fail;
+	}
+	*buf = addr_orig;
+	return 0;
+
+cleanup_fail:
+
+	if (addr_orig) {
+		munmap(addr_orig, bytes << 1);
+	}
+	close(fd);
+	return res;
+}
+
+int32_t
+qb_sys_fd_nonblock_cloexec_set(int32_t fd)
+{
+	int32_t res = 0;
+	int32_t oldflags = fcntl(fd, F_GETFD, 0);
+
+	if (oldflags < 0) {
+		oldflags = 0;
+	}
+	oldflags |= FD_CLOEXEC;
+	res = fcntl(fd, F_SETFD, oldflags);
+	if (res == -1) {
+		res = -errno;
+		qb_util_perror(LOG_ERR,
+			       "Could not set close-on-exit on fd:%d", fd);
+		return res;
+	}
+
+	res = fcntl(fd, F_SETFL, O_NONBLOCK);
+	if (res == -1) {
+		res = -errno;
+		qb_util_log(LOG_ERR, "Could not set non-blocking on fd:%d", fd);
+	}
+
+	return res;
+}
+
+void
+qb_sigpipe_ctl(enum qb_sigpipe_ctl ctl)
+{
+#if !defined(HAVE_MSG_NOSIGNAL) && !defined(HAVE_SO_NOSIGPIPE)
+	struct sigaction act;
+	struct sigaction oact;
+
+	act.sa_handler = SIG_IGN;
+
+	if (ctl == QB_SIGPIPE_IGNORE) {
+		sigaction(SIGPIPE, &act, &oact);
+	} else {
+		sigaction(SIGPIPE, &oact, NULL);
+	}
+#endif  /* !MSG_NOSIGNAL && !SO_NOSIGPIPE */
+}
+
+void
+qb_socket_nosigpipe(int32_t s)
+{
+#if !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SO_NOSIGPIPE)
+	int32_t on = 1;
+	setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(on));
+#endif /* !MSG_NOSIGNAL && SO_NOSIGPIPE */
+}
+
+
+/*
+ * atomic operations
+ * --------------------------------------------------------------------------
+ */
+#ifndef HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS
+/*
+ * We have to use the slow, but safe locking method
+ */
+
+static qb_thread_lock_t *qb_atomic_mutex = NULL;
+
+void
+qb_atomic_init(void)
+{
+	if (qb_atomic_mutex == NULL) {
+		qb_atomic_mutex = qb_thread_lock_create(QB_THREAD_LOCK_SHORT);
+	}
+	assert(qb_atomic_mutex);
+}
+
+int32_t
+qb_atomic_int_exchange_and_add(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+			       int32_t val)
+{
+	int32_t result;
+
+	qb_thread_lock(qb_atomic_mutex);
+	result = *atomic;
+	*atomic += val;
+	qb_thread_unlock(qb_atomic_mutex);
+
+	return result;
+}
+
+void
+qb_atomic_int_add(volatile int32_t QB_GNUC_MAY_ALIAS * atomic, int32_t val)
+{
+	qb_thread_lock(qb_atomic_mutex);
+	*atomic += val;
+	qb_thread_unlock(qb_atomic_mutex);
+}
+
+int32_t
+qb_atomic_int_compare_and_exchange(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+				   int32_t oldval, int32_t newval)
+{
+	int32_t result;
+
+	qb_thread_lock(qb_atomic_mutex);
+	if (*atomic == oldval) {
+		result = QB_TRUE;
+		*atomic = newval;
+	} else {
+		result = QB_FALSE;
+	}
+	qb_thread_unlock(qb_atomic_mutex);
+
+	return result;
+}
+
+int32_t
+qb_atomic_pointer_compare_and_exchange(volatile void *QB_GNUC_MAY_ALIAS *
+				       atomic, void *oldval, void *newval)
+{
+	int32_t result;
+
+	qb_thread_lock(qb_atomic_mutex);
+	if (*atomic == oldval) {
+		result = QB_TRUE;
+		*atomic = newval;
+	} else {
+		result = QB_FALSE;
+	}
+	qb_thread_unlock(qb_atomic_mutex);
+
+	return result;
+}
+
+#ifdef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+int32_t
+(qb_atomic_int_get) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic)
+{
+	int32_t result;
+
+	qb_thread_lock(qb_atomic_mutex);
+	result = *atomic;
+	qb_thread_unlock(qb_atomic_mutex);
+
+	return result;
+}
+
+void
+(qb_atomic_int_set) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+			  int32_t newval)
+{
+	qb_thread_lock(qb_atomic_mutex);
+	*atomic = newval;
+	qb_thread_unlock(qb_atomic_mutex);
+}
+
+void *
+(qb_atomic_pointer_get) (volatile void *QB_GNUC_MAY_ALIAS * atomic)
+{
+	void *result;
+
+	qb_thread_lock(qb_atomic_mutex);
+	result = (void*)*atomic;
+	qb_thread_unlock(qb_atomic_mutex);
+
+	return result;
+}
+
+void
+(qb_atomic_pointer_set) (volatile void *QB_GNUC_MAY_ALIAS * atomic,
+			      void *newval)
+{
+	qb_thread_lock(qb_atomic_mutex);
+	*atomic = newval;
+	qb_thread_unlock(qb_atomic_mutex);
+}
+#endif /* QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
+
+#else
+
+/*
+ * gcc built-ins
+ */
+
+void
+qb_atomic_init(void)
+{
+}
+
+int32_t
+qb_atomic_int_exchange_and_add(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+			       int32_t val)
+{
+	return __sync_fetch_and_add(atomic, val);
+}
+
+void
+qb_atomic_int_add(volatile int32_t QB_GNUC_MAY_ALIAS * atomic, int32_t val)
+{
+	__sync_fetch_and_add(atomic, val);
+}
+
+int32_t
+qb_atomic_int_compare_and_exchange(volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+				   int32_t oldval, int32_t newval)
+{
+	return __sync_bool_compare_and_swap(atomic, oldval, newval);
+}
+
+int32_t
+qb_atomic_pointer_compare_and_exchange(volatile void *QB_GNUC_MAY_ALIAS *
+				       atomic, void *oldval, void *newval)
+{
+	return __sync_bool_compare_and_swap(atomic, oldval, newval);
+}
+
+#ifdef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+#define QB_ATOMIC_MEMORY_BARRIER __sync_synchronize ()
+
+int32_t
+(qb_atomic_int_get) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic)
+{
+	QB_ATOMIC_MEMORY_BARRIER;
+	return *atomic;
+}
+
+void
+(qb_atomic_int_set) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+		     int32_t newval)
+{
+	*atomic = newval;
+	QB_ATOMIC_MEMORY_BARRIER;
+}
+
+void *
+(qb_atomic_pointer_get) (volatile void *QB_GNUC_MAY_ALIAS * atomic)
+{
+	QB_ATOMIC_MEMORY_BARRIER;
+	return (void*)*atomic;
+}
+
+void
+(qb_atomic_pointer_set) (volatile void *QB_GNUC_MAY_ALIAS * atomic,
+			 void *newval)
+{
+	*atomic = newval;
+	QB_ATOMIC_MEMORY_BARRIER;
+}
+
+#endif /* QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
+
+#endif /* HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS */
+
+#ifndef QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED
+int32_t
+(qb_atomic_int_get) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic)
+{
+	return qb_atomic_int_get(atomic);
+}
+
+void
+(qb_atomic_int_set) (volatile int32_t QB_GNUC_MAY_ALIAS * atomic,
+			  int32_t newval)
+{
+	qb_atomic_int_set(atomic, newval);
+}
+
+void *
+(qb_atomic_pointer_get) (volatile void *QB_GNUC_MAY_ALIAS * atomic)
+{
+	return qb_atomic_pointer_get(atomic);
+}
+
+void
+(qb_atomic_pointer_set) (volatile void *QB_GNUC_MAY_ALIAS * atomic,
+			      void *newval)
+{
+	qb_atomic_pointer_set(atomic, newval);
+}
+#endif /* !QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
diff --git a/lib/util.c b/lib/util.c
new file mode 100644
index 0000000..ef5ba25
--- /dev/null
+++ b/lib/util.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include "util_int.h"
+#include <pthread.h>
+#include <sys/stat.h>
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+
+struct qb_thread_lock_s {
+	qb_thread_lock_type_t type;
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	pthread_spinlock_t spinlock;
+#endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
+	pthread_mutex_t mutex;
+};
+
+qb_thread_lock_t *
+qb_thread_lock_create(qb_thread_lock_type_t type)
+{
+	struct qb_thread_lock_s *tl = malloc(sizeof(struct qb_thread_lock_s));
+	int32_t res;
+
+	if (tl == NULL) {
+		return NULL;
+	}
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	if (type == QB_THREAD_LOCK_SHORT) {
+		tl->type = QB_THREAD_LOCK_SHORT;
+		res = pthread_spin_init(&tl->spinlock, 1);
+	} else
+#endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
+	{
+		tl->type = QB_THREAD_LOCK_LONG;
+		res = pthread_mutex_init(&tl->mutex, NULL);
+	}
+	if (res == 0) {
+		return tl;
+	} else {
+		free(tl);
+		return NULL;
+	}
+}
+
+int32_t
+qb_thread_lock(qb_thread_lock_t * tl)
+{
+	int32_t res;
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	if (tl->type == QB_THREAD_LOCK_SHORT) {
+		res = -pthread_spin_lock(&tl->spinlock);
+	} else
+#endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
+	{
+		res = -pthread_mutex_lock(&tl->mutex);
+	}
+	return res;
+}
+
+int32_t
+qb_thread_unlock(qb_thread_lock_t * tl)
+{
+	int32_t res;
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	if (tl->type == QB_THREAD_LOCK_SHORT) {
+		res = -pthread_spin_unlock(&tl->spinlock);
+	} else
+#endif
+	{
+		res = -pthread_mutex_unlock(&tl->mutex);
+	}
+	return res;
+}
+
+int32_t
+qb_thread_trylock(qb_thread_lock_t * tl)
+{
+	int32_t res;
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	if (tl->type == QB_THREAD_LOCK_SHORT) {
+		res = -pthread_spin_trylock(&tl->spinlock);
+	} else
+#endif
+	{
+		res = -pthread_mutex_trylock(&tl->mutex);
+	}
+	return res;
+}
+
+int32_t
+qb_thread_lock_destroy(qb_thread_lock_t * tl)
+{
+	int32_t res;
+#ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
+	if (tl->type == QB_THREAD_LOCK_SHORT) {
+		res = -pthread_spin_destroy(&tl->spinlock);
+	} else
+#endif
+	{
+		res = -pthread_mutex_destroy(&tl->mutex);
+	}
+	free(tl);
+	return res;
+}
+
+void
+qb_timespec_add_ms(struct timespec *ts, int32_t ms)
+{
+#ifndef S_SPLINT_S
+	ts->tv_sec += ms / 1000;
+	ts->tv_nsec += (ms % 1000) * QB_TIME_NS_IN_MSEC;
+	if (ts->tv_nsec >= 1000000000L) {
+		ts->tv_sec++;
+		ts->tv_nsec = ts->tv_nsec - 1000000000L;
+	}
+#endif /* S_SPLINT_S */
+}
+
+#ifdef HAVE_MONOTONIC_CLOCK
+uint64_t
+qb_util_nano_current_get(void)
+{
+	uint64_t nano_monotonic;
+	struct timespec ts;
+
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	nano_monotonic =
+	    (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
+	return (nano_monotonic);
+}
+
+uint64_t
+qb_util_nano_from_epoch_get(void)
+{
+	uint64_t nano_monotonic;
+	struct timespec ts;
+#ifdef CLOCK_REALTIME_COARSE
+	clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+#else
+	clock_gettime(CLOCK_REALTIME, &ts);
+#endif
+	nano_monotonic =
+	    (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
+	return (nano_monotonic);
+}
+
+uint64_t
+qb_util_nano_monotonic_hz(void)
+{
+	uint64_t nano_monotonic_hz;
+	struct timespec ts;
+
+	clock_getres(CLOCK_MONOTONIC, &ts);
+
+	nano_monotonic_hz =
+	    QB_TIME_NS_IN_SEC / ((ts.tv_sec * QB_TIME_NS_IN_SEC) + ts.tv_nsec);
+
+	return (nano_monotonic_hz);
+}
+
+void
+qb_util_timespec_from_epoch_get(struct timespec *ts)
+{
+#ifdef CLOCK_REALTIME_COARSE
+	clock_gettime(CLOCK_REALTIME_COARSE, ts);
+#else
+	clock_gettime(CLOCK_REALTIME, ts);
+#endif
+}
+
+#else
+uint64_t
+qb_util_nano_current_get(void)
+{
+	return qb_util_nano_from_epoch_get();
+}
+
+uint64_t
+qb_util_nano_monotonic_hz(void)
+{
+	return sysconf(_SC_CLK_TCK);
+}
+
+void
+qb_util_timespec_from_epoch_get(struct timespec *ts)
+{
+	struct timeval time_from_epoch;
+	gettimeofday(&time_from_epoch, 0);
+
+#ifndef S_SPLINT_S
+	ts->tv_sec = time_from_epoch.tv_sec;
+	ts->tv_nsec = time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC;
+#endif /* S_SPLINT_S */
+}
+
+uint64_t
+qb_util_nano_from_epoch_get(void)
+{
+	uint64_t nano_from_epoch;
+	struct timeval time_from_epoch;
+	gettimeofday(&time_from_epoch, 0);
+
+	nano_from_epoch = ((time_from_epoch.tv_sec * QB_TIME_NS_IN_SEC) +
+			   (time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC));
+
+	return (nano_from_epoch);
+}
+#endif /* HAVE_MONOTONIC_CLOCK */
+
+struct qb_util_stopwatch {
+	uint64_t started;
+	uint64_t stopped;
+	uint32_t split_options;
+	uint32_t split_size;
+	uint32_t split_entries;
+	uint64_t *split_entry_list;
+};
+
+qb_util_stopwatch_t *
+qb_util_stopwatch_create(void)
+{
+	struct qb_util_stopwatch *sw;
+	sw = (struct qb_util_stopwatch *)calloc(1, sizeof(struct qb_util_stopwatch));
+	return sw;
+}
+
+void
+qb_util_stopwatch_free(qb_util_stopwatch_t * sw)
+{
+	free(sw->split_entry_list);
+	free(sw);
+}
+
+void
+qb_util_stopwatch_start(qb_util_stopwatch_t * sw)
+{
+	sw->started = qb_util_nano_current_get();
+	sw->stopped = 0;
+	sw->split_entries = 0;
+}
+
+void
+qb_util_stopwatch_stop(qb_util_stopwatch_t * sw)
+{
+	sw->stopped = qb_util_nano_current_get();
+}
+
+uint64_t
+qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t * sw)
+{
+	if (sw->stopped == 0 || sw->started == 0) {
+		return 0;
+	}
+	return ((sw->stopped - sw->started) / QB_TIME_NS_IN_USEC);
+}
+
+float
+qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
+{
+	uint64_t e6;
+	if (sw->stopped == 0 || sw->started == 0) {
+		return 0;
+	}
+	e6 = qb_util_stopwatch_us_elapsed_get(sw);
+	return ((float)e6 / (float)QB_TIME_US_IN_SEC);
+}
+
+int32_t
+qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
+        uint32_t max_splits, uint32_t options)
+{
+	sw->split_size = max_splits;
+	sw->split_options = options;
+	sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits);
+	if (sw->split_entry_list == NULL) {
+		return -errno;
+	}
+	return 0;
+}
+
+uint64_t
+qb_util_stopwatch_split(qb_util_stopwatch_t *sw)
+{
+	uint32_t new_entry_pos;
+	uint64_t time_start;
+	uint64_t time_end;
+
+	if (sw->split_size == 0) {
+		return 0;
+	}
+	if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) &&
+	    sw->split_entries == sw->split_size) {
+		return 0;
+	}
+	if (sw->started == 0) {
+		qb_util_stopwatch_start(sw);
+	}
+	new_entry_pos = sw->split_entries % (sw->split_size);
+	sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get();
+	sw->split_entries++;
+
+	time_start = sw->split_entry_list[new_entry_pos];
+	if (sw->split_entries == 1) {
+		/* first entry */
+		time_end = sw->started;
+	} else if (new_entry_pos == 0) {
+		/* wrap around */
+		time_end = sw->split_entry_list[sw->split_size - 1];
+	} else {
+		time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size];
+	}
+	return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
+
+uint32_t
+qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw)
+{
+	if (sw->split_entries) {
+		return sw->split_entries - 1;
+	}
+	return sw->split_entries;
+}
+
+uint64_t
+qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
+				 uint32_t receint, uint32_t older)
+{
+	uint64_t time_start;
+	uint64_t time_end;
+
+	if (sw->started == 0 ||
+	    receint >= sw->split_entries ||
+	    older >= sw->split_entries ||
+	    receint < older) {
+		return 0;
+	}
+	if (sw->split_options & QB_UTIL_SW_OVERWRITE &&
+	    (receint < (sw->split_entries - sw->split_size) ||
+	     older < (sw->split_entries - sw->split_size))) {
+		return 0;
+	}
+
+	time_start = sw->split_entry_list[receint % (sw->split_size)];
+	if (older == receint) {
+		time_end = sw->started;
+	} else {
+		time_end = sw->split_entry_list[older % (sw->split_size)];
+	}
+	return (time_start - time_end) / QB_TIME_NS_IN_USEC;
+}
diff --git a/lib/util_int.h b/lib/util_int.h
new file mode 100644
index 0000000..5c854a2
--- /dev/null
+++ b/lib/util_int.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QB_UTIL_INT_H_DEFINED
+#define QB_UTIL_INT_H_DEFINED
+
+#include "os_base.h"
+#include <qb/qblog.h>
+
+#if !defined (va_copy)
+#if defined (__va_copy)
+#define va_copy(_a, _b) __va_copy(_a, _b)
+#else
+#define va_copy(_a, _b)  memcpy(&_a, &_b, sizeof(va_list))
+#endif /* !__va_copy */
+#endif /* !va_copy */
+
+/**
+ * This is used internally by libqb.
+ *
+ * It sets the 32nd bit of the tags so that internal logs can be
+ * destinguished from external ones.
+ */
+#ifndef S_SPLINT_S
+#define qb_util_log(priority, fmt, args...) qb_logt(priority, QB_LOG_TAG_LIBQB_MSG, fmt, ##args)
+#else
+#define qb_util_log
+#endif
+
+#ifndef S_SPLINT_S
+#define qb_util_perror(priority, fmt, args...) do {		\
+	char _perr_buf_[QB_LOG_STRERROR_MAX_LEN];			\
+	const char *_perr_str_ = qb_strerror_r(errno, _perr_buf_, sizeof(_perr_buf_));	\
+	qb_logt(priority, QB_LOG_TAG_LIBQB_MSG, fmt ": %s (%d)", ##args, _perr_str_, errno); \
+    } while(0)
+#else
+#define qb_util_perror
+#endif
+
+/**
+ * Create a file to be used to back shared memory.
+ *
+ * @param path (out) the final absolute path of the file.
+ * @param file (in) the name of the file to be used.
+ * @param bytes the size to truncate the file to.
+ * @param file_flags same as passed into open()
+ * @return 0 (success) or -errno
+ */
+int32_t qb_sys_mmap_file_open(char *path, const char *file, size_t bytes,
+			       uint32_t file_flags);
+
+/**
+ * Create a shared mamory circular buffer.
+ *
+ * @param fd an open file to use to back the shared memory.
+ * @param buf (out) the pointer to the start of the memory.
+ * @param bytes the size of the shared memory.
+ * @return 0 (success) or -errno
+ */
+int32_t qb_sys_circular_mmap(int32_t fd, void **buf, size_t bytes);
+
+
+/**
+ * Set O_NONBLOCK and FD_CLOEXEC on a file descriptor.
+ * @param fd the file descriptor.
+ * @return 0 (success) or -errno
+ */
+int32_t qb_sys_fd_nonblock_cloexec_set(int32_t fd);
+
+enum qb_sigpipe_ctl {
+       QB_SIGPIPE_IGNORE,
+       QB_SIGPIPE_DEFAULT,
+};
+
+/**
+ * Control sigpipe (ignore/default) during send/recv
+ * Needed on some bsd's
+ */
+void qb_sigpipe_ctl(enum qb_sigpipe_ctl ctl);
+
+/**
+ * Control sigpipe on the socket.
+ */
+void qb_socket_nosigpipe(int32_t s);
+
+#define SERVER_BACKLOG 128
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX    108
+#endif /* UNIX_PATH_MAX */
+
+/*
+ * SUN_LEN() does a strlen() on sun_path, but if you are trying to use the
+ * "Linux abstract namespace" (you have to set sun_path[0] == '\0') then
+ * the strlen() doesn't work.
+ */
+#if defined(SUN_LEN)
+#define QB_SUN_LEN(a) ((a)->sun_path[0] == '\0') ? sizeof(*(a)) : SUN_LEN(a)
+#else
+#define QB_SUN_LEN(a) sizeof(*(a))
+#endif
+
+#endif /* QB_UTIL_INT_H_DEFINED */
diff --git a/libqb.spec.in b/libqb.spec.in
new file mode 100644
index 0000000..2d904f6
--- /dev/null
+++ b/libqb.spec.in
@@ -0,0 +1,72 @@
+%global alphatag @alphatag@
+%global numcomm @numcomm@
+%global dirty @dirty@
+
+Name:           libqb
+Version:        @version@
+Release:        1%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist}
+Summary:        An IPC library for high performance servers.
+
+Group:          System Environment/Libraries
+License:        LGPLv2+
+URL:            https://github.com/ClusterLabs/libqb
+Source0:        https://fedorahosted.org/releases/q/u/quarterback/%{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}.tar.gz
+BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires:  autoconf automake libtool doxygen procps check-devel
+
+%description
+libqb provides high performance client server reusable features.
+Initially these are IPC and poll.
+
+%prep
+%setup -q -n %{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}
+
+%build
+./autogen.sh
+%configure --disable-static
+make %{?_smp_mflags}
+
+%check
+make check
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
+rm -rf $RPM_BUILD_ROOT/%{_datadir}/doc/libqb
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING
+%{_sbindir}/qb-blackbox
+%{_libdir}/libqb.so.*
+
+%package        devel
+Summary:        Development files for %{name}
+Group:          Development/Libraries
+Requires:       %{name} = %{version}-%{release} pkgconfig
+
+%description    devel
+The %{name}-devel package contains libraries and header files for
+developing applications that use %{name}.
+
+%files devel
+%defattr(-,root,root,-)
+%doc COPYING README.markdown
+%{_includedir}/qb/
+%{_libdir}/libqb.so
+%{_libdir}/pkgconfig/libqb.pc
+%{_mandir}/man3/qb*3*
+%{_mandir}/man8/qb-blackbox.8.gz
+
+%changelog
+* @date@ Autotools generated version <nobody at nowhere.org> - @version at -1-@numcomm at .@alphatag at .@dirty@
+- Autotools generated version
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..9ce7536
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,15 @@
+*.test
+*.fdata
+bench-log
+bmc
+bmcpt
+bms
+loop
+rbreader
+rbwriter
+libqb
+auto_*
+format_compare_speed
+crash_test_dummy
+file_change_bytes
+test.conf
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..2ebc46c
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,145 @@
+# Copyright (c) 2010 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+#
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST =
+CLEANFILES =
+AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+
+noinst_PROGRAMS = bmc bmcpt bms rbwriter rbreader loop bench-log \
+	auto_check_header_qbarray auto_check_header_qbconfig auto_check_header_qbhdb \
+	auto_check_header_qbipc_common auto_check_header_qblist auto_check_header_qbloop \
+	auto_check_header_qbrb auto_check_header_qbatomic auto_check_header_qbdefs \
+	auto_check_header_qbipcc auto_check_header_qbipcs auto_check_header_qblog \
+	auto_check_header_qbmap auto_check_header_qbutil format_compare_speed
+
+format_compare_speed_SOURCES = format_compare_speed.c $(top_builddir)/include/qb/qbutil.h
+format_compare_speed_LDADD = $(top_builddir)/lib/libqb.la
+
+bmc_SOURCES = bmc.c $(top_builddir)/include/qb/qbipcc.h
+bmc_LDADD = $(top_builddir)/lib/libqb.la
+
+bmcpt_SOURCES = bmcpt.c $(top_builddir)/include/qb/qbipcc.h
+bmcpt_LDADD = $(top_builddir)/lib/libqb.la
+
+bms_SOURCES = bms.c $(top_builddir)/include/qb/qbipcs.h
+bms_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include $(GLIB_CFLAGS)
+bms_LDADD = $(top_builddir)/lib/libqb.la $(GLIB_LIBS)
+
+rbwriter_SOURCES = rbwriter.c $(top_builddir)/include/qb/qbrb.h
+rbwriter_LDADD = $(top_builddir)/lib/libqb.la
+
+rbreader_SOURCES = rbreader.c $(top_builddir)/include/qb/qbrb.h
+rbreader_LDADD = $(top_builddir)/lib/libqb.la
+
+loop_SOURCES = loop.c $(top_builddir)/include/qb/qbloop.h
+loop_LDADD = $(top_builddir)/lib/libqb.la
+
+public_headers = $(wildcard $(top_srcdir)/include/qb/qb*.h)
+auto_c_files = $(patsubst %.h,auto_check_header_%.c,$(public_headers))
+CLEANFILES += $(auto_c_files)
+MAINTAINERCLEANFILES += $(auto_c_files)
+
+$(builddir)/auto_check_header_%.c: $(top_srcdir)/include/qb/%.h
+	@name=$$(echo "$<" | sed  "s|.*qb/qb||" | sed "s|\.h||") ;\
+	NAME=$$(echo $$name | tr [:lower:] [:upper:]) ;\
+	echo "#include <qb/qb$$name.h>" > $@_ ;\
+	echo "#ifndef QB_$${NAME}_H_DEFINED" >> $@_ ;\
+	echo "#error no header protector in file qb$$name.h" >> $@_ ;\
+	echo "#endif" >> $@_ ;\
+	echo "int main(void) {return 0;}" >> $@_
+	$(AM_V_GEN)mv $@_ $@
+
+distclean-compile:
+	rm -rf auto_*.c
+
+if HAVE_DICT_WORDS
+if HAVE_SLOW_TESTS
+EXTRA_DIST += make-log-test.sh
+CLEANFILES += auto_write_logs.c
+MAINTAINERCLEANFILES += auto_write_logs.c
+
+nodist_bench_log_SOURCES = auto_write_logs.c
+
+bench_log: auto_write_logs.c
+$(builddir)/auto_write_logs.c: make-log-test.sh
+	$(srcdir)/make-log-test.sh > $(builddir)/write_logs-tmp.c
+	$(AM_V_GEN)mv $(builddir)/write_logs-tmp.c $(builddir)/auto_write_logs.c
+endif
+endif
+
+bench_log_SOURCES = bench-log.c $(top_builddir)/include/qb/qblog.h
+bench_log_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+bench_log_LDADD = $(top_builddir)/lib/libqb.la
+
+if HAVE_CHECK
+EXTRA_DIST += resources.test
+EXTRA_DIST += blackbox-segfault.sh
+
+TESTS = array.test map.test rb.test log.test blackbox-segfault.sh loop.test ipc.test resources.test
+
+resources.log: rb.log log.log ipc.log
+
+check_PROGRAMS = array.test map.test rb.test log.test loop.test ipc.test util.test crash_test_dummy file_change_bytes
+check_SCRIPTS = resources.test blackbox-segfault.sh
+
+if HAVE_SLOW_TESTS
+TESTS += util.test
+check_PROGRAMS += util.test
+endif
+
+file_change_bytes_SOURCES = file_change_bytes.c
+
+crash_test_dummy_SOURCES = crash_test_dummy.c $(top_builddir)/include/qb/qblog.h
+crash_test_dummy_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+crash_test_dummy_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+array_test_SOURCES = check_array.c $(top_builddir)/include/qb/qbarray.h
+array_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+array_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+map_test_SOURCES = check_map.c $(top_builddir)/include/qb/qbmap.h
+map_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+map_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+rb_test_SOURCES = check_rb.c $(top_builddir)/include/qb/qbrb.h
+rb_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+rb_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+loop_test_SOURCES = check_loop.c $(top_builddir)/include/qb/qbloop.h
+loop_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+loop_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+ipc_test_SOURCES = check_ipc.c $(top_builddir)/include/qb/qbipcc.h $(top_builddir)/include/qb/qbipcs.h
+ipc_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+ipc_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+log_test_SOURCES = check_log.c $(top_builddir)/include/qb/qblog.h
+log_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+log_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+util_test_SOURCES = check_util.c $(top_builddir)/include/qb/qbutil.h
+util_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
+util_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@
+
+endif
+
+clean-generic:
+	$(AM_V_GEN)rm -f *.log
+	$(AM_V_GEN)rm -f *.fdata
diff --git a/tests/bench-log.c b/tests/bench-log.c
new file mode 100644
index 0000000..7d54439
--- /dev/null
+++ b/tests/bench-log.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+#define ITERATIONS 50000
+static qb_util_stopwatch_t *sw;
+
+extern void log_dict_words(void);
+
+static void
+bm_finish (const char *operation)
+{
+	qb_util_stopwatch_stop(sw);
+
+	if (strlen (operation) > 22) {
+		printf ("%s\t\t", operation);
+	} else {
+		printf ("%s\t\t\t", operation);
+	}
+	printf("%9.3f operations/sec\n",
+	       ((float)ITERATIONS) /  qb_util_stopwatch_sec_elapsed_get(sw));
+}
+
+int
+main(void)
+{
+	int i;
+
+	sw =  qb_util_stopwatch_create();
+	qb_log_init("simple-log", LOG_USER, LOG_INFO);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, QB_TRUE);
+
+	qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
+	QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 128000);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_THREADED, QB_FALSE);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	printf ("heating up cache with qb_log functionality\n");
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_DEBUG, "hello");
+	}
+	qb_util_stopwatch_start(sw);
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_DEBUG, "RecordA");
+	}
+	bm_finish ("qb_log 1 arguments:");
+	qb_util_stopwatch_start(sw);
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_DEBUG, "%s%s", "RecordA", "RecordB");
+	}
+	bm_finish ("qb_log 2 args(str):");
+	qb_util_stopwatch_start(sw);
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_DEBUG, "%s%s%s", "RecordA", "RecordB", "RecordC");
+	}
+	bm_finish ("qb_log 3 args(str):");
+	qb_util_stopwatch_start(sw);
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_DEBUG, "%i %u %p", -534, 4508, &i);
+	}
+	bm_finish ("qb_log 3 args(int):");
+#if defined(HAVE_DICT_WORDS) && defined(HAVE_SLOW_TESTS)
+	qb_util_stopwatch_start(sw);
+	log_dict_words();
+	bm_finish ("qb_log /usr/share/dict/words:");
+#endif /* HAVE_DICT_WORDS */
+
+	/* this will close the ringbuffer
+	 */
+	qb_log_fini();
+
+	return 0;
+}
diff --git a/tests/blackbox-segfault.sh b/tests/blackbox-segfault.sh
new file mode 100755
index 0000000..0e1c47c
--- /dev/null
+++ b/tests/blackbox-segfault.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# create a normal blackbox
+rm -f crash-test-dummy.fdata
+./crash_test_dummy
+
+. ./test.conf
+
+# first test that reading the valid
+# blackbox data actually works.
+../tools/qb-blackbox crash-test-dummy.fdata
+if [ $? -ne 0 ]; then
+	exit 1
+fi
+
+
+for i in $(seq $NUM_BB_TESTS)
+do
+    rm -f butchered_blackbox.fdata
+    echo " ==== Corrupt blackbox test $i/$NUM_BB_TESTS ===="
+    ./file_change_bytes -i crash-test-dummy.fdata -o butchered_blackbox.fdata -n 1024
+    ../tools/qb-blackbox butchered_blackbox.fdata
+    [ $? -gt 127 ] && exit 1 || true
+done
+
+exit 0
diff --git a/tests/bmc.c b/tests/bmc.c
new file mode 100644
index 0000000..585811c
--- /dev/null
+++ b/tests/bmc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake (sdake at redhat.com)
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+
+#include <qb/qblog.h>
+#include <qb/qbutil.h>
+#include <qb/qbipcc.h>
+
+#define ITERATIONS 10000
+pid_t mypid;
+int32_t blocking = QB_TRUE;
+int32_t events = QB_FALSE;
+int32_t verbose = 0;
+static qb_ipcc_connection_t *conn;
+#define MAX_MSG_SIZE (8192*128)
+static qb_util_stopwatch_t *sw;
+
+static void bm_finish(const char *operation, int32_t size)
+{
+	float ops_per_sec;
+	float mbs_per_sec;
+	float elapsed;
+
+	qb_util_stopwatch_stop(sw);
+	elapsed = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops_per_sec = ((float)ITERATIONS) / elapsed;
+	mbs_per_sec = ((((float)ITERATIONS) * size) / elapsed) / (1024.0 * 1024.0);
+
+	qb_log(LOG_INFO, "write size, %d, OPs/sec, %9.3f, MB/sec, %9.3f",
+	       size, ops_per_sec, mbs_per_sec);
+}
+
+struct my_req {
+	struct qb_ipc_request_header hdr;
+	char message[1024 * 1024];
+};
+
+static struct my_req request;
+
+static int32_t bmc_send_nozc(uint32_t size)
+{
+	struct qb_ipc_response_header res_header;
+	int32_t res;
+
+	request.hdr.id = QB_IPC_MSG_USER_START + 3;
+	request.hdr.size = sizeof(struct qb_ipc_request_header) + size;
+
+repeat_send:
+	res = qb_ipcc_send(conn, &request, request.hdr.size);
+	if (res < 0) {
+		if (res == -EAGAIN) {
+			goto repeat_send;
+		} else if (res == -EINVAL || res == -EINTR || res == -ENOTCONN) {
+			qb_perror(LOG_ERR, "qb_ipcc_send");
+			return -1;
+		} else {
+			errno = -res;
+			qb_perror(LOG_ERR, "qb_ipcc_send");
+			goto repeat_send;
+		}
+	}
+
+	if (blocking) {
+		res = qb_ipcc_recv(conn,
+				&res_header,
+				sizeof(struct qb_ipc_response_header), -1);
+		if (res == -EINTR) {
+			return -1;
+		}
+		if (res < 0) {
+			qb_perror(LOG_ERR, "qb_ipcc_recv");
+		}
+		assert(res == sizeof(struct qb_ipc_response_header));
+		assert(res_header.id == 13);
+		assert(res_header.size == sizeof(struct qb_ipc_response_header));
+	}
+	if (events) {
+		res = qb_ipcc_event_recv(conn,
+				&res_header,
+				sizeof(struct qb_ipc_response_header), -1);
+		if (res == -EINTR) {
+			return -1;
+		}
+		if (res < 0) {
+			qb_perror(LOG_ERR, "qb_ipcc_event_recv");
+		}
+		assert(res == sizeof(struct qb_ipc_response_header));
+		assert(res_header.id == 13);
+		assert(res_header.size == sizeof(struct qb_ipc_response_header));
+	}
+	return 0;
+}
+
+struct qb_ipc_request_header *global_zcb_buffer;
+
+static void show_usage(const char *name)
+{
+	qb_log(LOG_INFO, "usage: \n");
+	qb_log(LOG_INFO, "%s <options>\n", name);
+	qb_log(LOG_INFO, "\n");
+	qb_log(LOG_INFO, "  options:\n");
+	qb_log(LOG_INFO, "\n");
+	qb_log(LOG_INFO, "  -n             non-blocking ipc (default blocking)\n");
+	qb_log(LOG_INFO, "  -e             receive events\n");
+	qb_log(LOG_INFO, "  -v             verbose\n");
+	qb_log(LOG_INFO, "  -h             show this help text\n");
+	qb_log(LOG_INFO, "\n");
+}
+
+static void sigterm_handler(int32_t num)
+{
+	qb_log(LOG_INFO, "bmc: %s(%d)\n", __func__, num);
+	qb_ipcc_disconnect(conn);
+	exit(0);
+}
+
+int32_t
+main(int32_t argc, char *argv[])
+{
+	const char *options = "nevh";
+	int32_t opt;
+	int32_t i, j;
+	size_t size;
+
+	mypid = getpid();
+
+	qb_log_init("bmc", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'n':
+			blocking = QB_FALSE;
+			break;
+		case 'e':
+			events = QB_TRUE;
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+
+	signal(SIGINT, sigterm_handler);
+	signal(SIGILL, sigterm_handler);
+	signal(SIGTERM, sigterm_handler);
+	conn = qb_ipcc_connect("bm1", MAX_MSG_SIZE);
+	if (conn == NULL) {
+		qb_perror(LOG_ERR, "qb_ipcc_connect");
+		exit(1);
+	}
+
+	sw =  qb_util_stopwatch_create();
+	size = QB_MAX(sizeof(struct qb_ipc_request_header), 64);
+	for (j = 0; j < 20; j++) {
+		if (size >= MAX_MSG_SIZE)
+			break;
+		qb_util_stopwatch_start(sw);
+		for (i = 0; i < ITERATIONS; i++) {
+			if (bmc_send_nozc(size) == -1) {
+				break;
+			}
+		}
+		bm_finish("send_nozc", size);
+		size *= 2;
+	}
+
+	qb_ipcc_disconnect(conn);
+	return EXIT_SUCCESS;
+}
+
diff --git a/tests/bmcpt.c b/tests/bmcpt.c
new file mode 100644
index 0000000..2f2d276
--- /dev/null
+++ b/tests/bmcpt.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake (sdake at redhat.com)
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qbipcc.h>
+
+#define ITERATIONS 10000000
+#define THREADS 4
+
+struct bm_ctx {
+	qb_ipcc_connection_t *conn;
+	qb_util_stopwatch_t *sw;
+	float mbs;
+	float secs;
+	int32_t multi;
+	uint32_t counter;
+};
+
+static void bm_start(struct bm_ctx *ctx)
+{
+	qb_util_stopwatch_start(ctx->sw);
+}
+
+static void bm_finish(struct bm_ctx *ctx, const char *operation, int32_t size)
+{
+	qb_util_stopwatch_stop(ctx->sw);
+	ctx->secs = qb_util_stopwatch_sec_elapsed_get(ctx->sw);
+
+	ctx->mbs =
+	    ((((float)ctx->counter) * size) / ctx->secs) / (1024.0 *
+							    1024.0);
+}
+
+static void bmc_connect(struct bm_ctx *ctx)
+{
+	ctx->sw = qb_util_stopwatch_create();
+	ctx->conn = qb_ipcc_connect("bm1", QB_MAX(1000 * (100 + THREADS),
+						  1024*1024));
+	if (ctx->conn == NULL) {
+		perror("qb_ipcc_connect");
+		exit(-1);
+	}
+}
+
+static void bmc_disconnect(struct bm_ctx *ctx)
+{
+	qb_ipcc_disconnect(ctx->conn);
+	qb_util_stopwatch_free(ctx->sw);
+}
+
+struct my_req {
+	struct qb_ipc_request_header hdr;
+	char message[1024 * 1024];
+};
+
+static struct my_req request;
+
+static int32_t bmc_send_nozc(struct bm_ctx *ctx, uint32_t size)
+{
+	struct qb_ipc_response_header res_header;
+	int32_t res;
+
+	request.hdr.id = QB_IPC_MSG_USER_START + 3;
+	request.hdr.size = sizeof(struct qb_ipc_request_header) + size;
+
+repeat_send:
+	res = qb_ipcc_send(ctx->conn, &request, request.hdr.size);
+	if (res < 0) {
+		if (res == -EAGAIN) {
+			goto repeat_send;
+		} else if (res == -EINVAL || res == -EINTR) {
+			perror("qb_ipcc_send");
+			return -1;
+		} else {
+			errno = -res;
+			perror("qb_ipcc_send");
+			goto repeat_send;
+		}
+	}
+
+	res = qb_ipcc_recv(ctx->conn, &res_header,
+			   sizeof(struct qb_ipc_response_header), -1);
+	if (res == -EINTR) {
+		return -1;
+	}
+	if (res < 0) {
+		perror("qb_ipcc_recv");
+	}
+	assert(res == sizeof(struct qb_ipc_response_header));
+	assert(res_header.id == 13);
+	assert(res_header.size == sizeof(struct qb_ipc_response_header));
+	return 0;
+}
+
+uint32_t alarm_notice = 0;
+static void sigalrm_handler(int32_t num)
+{
+	alarm_notice = 1;
+}
+
+static void *benchmark(void *ctx)
+{
+	struct bm_ctx *bm_ctx = (struct bm_ctx *)ctx;
+	int32_t res;
+
+	bmc_connect(bm_ctx);
+
+	bm_start(bm_ctx);
+	for (;;) {
+		bm_ctx->counter++;
+		res = bmc_send_nozc(bm_ctx, 1000 * bm_ctx->multi);
+		if (alarm_notice || res == -1) {
+			bm_finish(bm_ctx, "send_nozc", 1000 * bm_ctx->multi);
+			bmc_disconnect(bm_ctx);
+			return (NULL);
+		}
+	}
+}
+
+
+int32_t main(void)
+{
+	struct bm_ctx bm_ctx[THREADS];
+	pthread_t threads[THREADS];
+	pthread_attr_t thread_attr[THREADS];
+	int32_t i, j;
+	float total_mbs;
+	void *retval;
+
+	for (i = 0; i < THREADS; i++) {
+		bm_ctx[i].mbs = 0;
+	}
+	signal(SIGALRM, sigalrm_handler);
+	for (j = 0; j < 500; j++) {
+		alarm_notice = 0;
+		alarm(3);
+		for (i = 0; i < THREADS; i++) {
+			bm_ctx[i].multi = j + 100;
+			bm_ctx[i].counter = 0;
+			pthread_attr_init(&thread_attr[i]);
+
+			pthread_attr_setdetachstate(&thread_attr[i],
+						    PTHREAD_CREATE_JOINABLE);
+			pthread_create(&threads[i], &thread_attr[i], benchmark,
+				       &bm_ctx[i]);
+		}
+		for (i = 0; i < THREADS; i++) {
+			pthread_join(threads[i], &retval);
+		}
+		total_mbs = 0;
+		for (i = 0; i < THREADS; i++) {
+			total_mbs = total_mbs + bm_ctx[i].mbs;
+		}
+		printf("%d ", 1000 * bm_ctx[0].multi);
+		printf("%9.3f\n", total_mbs);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/tests/bms.c b/tests/bms.c
new file mode 100644
index 0000000..a965599
--- /dev/null
+++ b/tests/bms.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2006-2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <qb/qbutil.h>
+#include <qb/qbloop.h>
+#include <qb/qbipcs.h>
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+
+int32_t blocking = QB_TRUE;
+int32_t events = QB_FALSE;
+int32_t use_glib = QB_FALSE;
+int32_t verbose = 0;
+
+static qb_loop_t *bms_loop;
+#ifdef HAVE_GLIB
+static GMainLoop *glib_loop;
+static qb_array_t *gio_map;
+#endif
+static qb_ipcs_service_t* s1;
+
+static int32_t s1_connection_accept_fn(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
+{
+#if 0
+	if (uid == 0 && gid == 0) {
+		if (verbose) {
+			qb_log(LOG_INFO, "%s:%d %s authenticated connection\n",
+					__FILE__, __LINE__, __func__);
+		}
+		return 1;
+	}
+	qb_log(LOG_INFO, "%s:%d %s() BAD user!\n", __FILE__, __LINE__, __func__);
+	return 0;
+#else
+	return 0;
+#endif
+}
+
+
+static void s1_connection_created_fn(qb_ipcs_connection_t *c)
+{
+	struct qb_ipcs_stats srv_stats;
+
+	qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
+	qb_log(LOG_NOTICE, "Connection created > active:%d > closed:%d",
+	       srv_stats.active_connections,
+	       srv_stats.closed_connections);
+}
+
+static void s1_connection_destroyed_fn(qb_ipcs_connection_t *c)
+{
+	qb_log(LOG_INFO, "connection about to be freed\n");
+}
+
+static int32_t s1_connection_closed_fn(qb_ipcs_connection_t *c)
+{
+	struct qb_ipcs_connection_stats stats;
+	struct qb_ipcs_stats srv_stats;
+
+	qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
+
+	qb_ipcs_connection_stats_get(c, &stats, QB_FALSE);
+
+	qb_log(LOG_INFO, "Connection to pid:%d destroyed > active:%d > closed:%d",
+	       stats.client_pid,
+	       srv_stats.active_connections,
+	       srv_stats.closed_connections);
+
+	qb_log(LOG_INFO, " Requests     %"PRIu64"\n", stats.requests);
+	qb_log(LOG_INFO, " Responses    %"PRIu64"\n", stats.responses);
+	qb_log(LOG_INFO, " Events       %"PRIu64"\n", stats.events);
+	qb_log(LOG_INFO, " Send retries %"PRIu64"\n", stats.send_retries);
+	qb_log(LOG_INFO, " Recv retries %"PRIu64"\n", stats.recv_retries);
+	qb_log(LOG_INFO, " FC state     %d\n", stats.flow_control_state);
+	qb_log(LOG_INFO, " FC count     %"PRIu64"\n\n", stats.flow_control_count);
+	return 0;
+}
+
+static int32_t s1_msg_process_fn(qb_ipcs_connection_t *c,
+		void *data, size_t size)
+{
+	struct qb_ipc_request_header *req_pt = (struct qb_ipc_request_header *)data;
+	struct qb_ipc_response_header response;
+	ssize_t res;
+
+	qb_log(LOG_TRACE, "msg:%d, size:%d",
+	       req_pt->id, req_pt->size);
+	response.size = sizeof(struct qb_ipc_response_header);
+	response.id = 13;
+	response.error = 0;
+	if (blocking) {
+		res = qb_ipcs_response_send(c, &response,
+				sizeof(response));
+		if (res < 0) {
+			qb_perror(LOG_ERR, "qb_ipcs_response_send");
+			return res;
+		}
+	}
+	if (events) {
+		res = qb_ipcs_event_send(c, &response,
+				sizeof(response));
+		if (res < 0) {
+			qb_perror(LOG_ERR, "qb_ipcs_event_send");
+			return res;
+		}
+	}
+	return 0;
+}
+
+static void sigusr1_handler(int32_t num)
+{
+	qb_log(LOG_INFO, "%s(%d)\n", __func__, num);
+	qb_ipcs_destroy(s1);
+	exit(0);
+}
+
+static void show_usage(const char *name)
+{
+	qb_log(LOG_INFO, "usage: \n");
+	qb_log(LOG_INFO, "%s <options>\n", name);
+	qb_log(LOG_INFO, "\n");
+	qb_log(LOG_INFO, "  options:\n");
+	qb_log(LOG_INFO, "\n");
+	qb_log(LOG_INFO, "  -n             non-blocking ipc (default blocking)\n");
+	qb_log(LOG_INFO, "  -e             send events back instead for responses\n");
+	qb_log(LOG_INFO, "  -v             verbose\n");
+	qb_log(LOG_INFO, "  -h             show this help text\n");
+	qb_log(LOG_INFO, "  -m             use shared memory\n");
+	qb_log(LOG_INFO, "  -p             use posix message queues\n");
+	qb_log(LOG_INFO, "  -s             use sysv message queues\n");
+	qb_log(LOG_INFO, "  -u             use unix sockets\n");
+	qb_log(LOG_INFO, "  -g             use glib mainloop\n");
+	qb_log(LOG_INFO, "\n");
+}
+
+#ifdef HAVE_GLIB
+
+struct gio_to_qb_poll {
+	gboolean is_used;
+	GIOChannel *channel;
+	int32_t events;
+	void * data;
+	qb_ipcs_dispatch_fn_t fn;
+	enum qb_loop_priority p;
+};
+
+
+static gboolean
+gio_read_socket (GIOChannel *gio, GIOCondition condition, gpointer data)
+{
+	struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
+	gint fd = g_io_channel_unix_get_fd(gio);
+
+	return (adaptor->fn(fd, condition, adaptor->data) == 0);
+}
+
+static int32_t my_g_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	struct gio_to_qb_poll *adaptor;
+	GIOChannel *channel;
+	int32_t res = 0;
+
+	res = qb_array_grow(gio_map, fd + 1);
+	if (res < 0) {
+		return res;
+	}
+	res = qb_array_index(gio_map, fd, (void**)&adaptor);
+	if (res < 0) {
+		return res;
+	}
+	if (adaptor->is_used) {
+		return -EEXIST;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+	if (!channel) {
+		return -ENOMEM;
+	}
+
+	adaptor->channel = channel;
+	adaptor->fn = fn;
+	adaptor->events = evts;
+	adaptor->data = data;
+	adaptor->p = p;
+	adaptor->is_used = TRUE;
+
+	g_io_add_watch(channel, evts, gio_read_socket, adaptor);
+	return 0;
+}
+
+static int32_t my_g_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return 0;
+}
+
+static int32_t my_g_dispatch_del(int32_t fd)
+{
+	struct gio_to_qb_poll *adaptor;
+	if (qb_array_index(gio_map, fd, (void**)&adaptor) == 0) {
+		g_io_channel_unref(adaptor->channel);
+		adaptor->is_used = FALSE;
+	}
+	return 0;
+}
+
+#endif /* HAVE_GLIB */
+
+static int32_t my_job_add(enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn fn)
+{
+	return qb_loop_job_add(bms_loop, p, data, fn);
+}
+
+static int32_t my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_add(bms_loop, p, fd, evts, data, fn);
+}
+
+static int32_t my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_mod(bms_loop, p, fd, evts, data, fn);
+}
+
+static int32_t my_dispatch_del(int32_t fd)
+{
+	return qb_loop_poll_del(bms_loop, fd);
+}
+
+
+int32_t main(int32_t argc, char *argv[])
+{
+	const char *options = "nevhmpsug";
+	int32_t opt;
+	int32_t rc;
+	enum qb_ipc_type ipc_type = QB_IPC_SHM;
+	struct qb_ipcs_service_handlers sh = {
+		.connection_accept = s1_connection_accept_fn,
+		.connection_created = s1_connection_created_fn,
+		.msg_process = s1_msg_process_fn,
+		.connection_destroyed = s1_connection_destroyed_fn,
+		.connection_closed = s1_connection_closed_fn,
+	};
+	struct qb_ipcs_poll_handlers ph = {
+		.job_add = my_job_add,
+		.dispatch_add = my_dispatch_add,
+		.dispatch_mod = my_dispatch_mod,
+		.dispatch_del = my_dispatch_del,
+	};
+#ifdef HAVE_GLIB
+	struct qb_ipcs_poll_handlers glib_ph = {
+		.job_add = NULL, /* FIXME */
+		.dispatch_add = my_g_dispatch_add,
+		.dispatch_mod = my_g_dispatch_mod,
+		.dispatch_del = my_g_dispatch_del,
+	};
+#endif /* HAVE_GLIB */
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'm':
+			ipc_type = QB_IPC_SHM;
+			break;
+		case 'u':
+			ipc_type = QB_IPC_SOCKET;
+			break;
+		case 'n':	/* non-blocking */
+			blocking = QB_FALSE;
+			break;
+		case 'e':	/* events */
+			events = QB_TRUE;
+			break;
+		case 'g':
+			use_glib = QB_TRUE;
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+	signal(SIGINT, sigusr1_handler);
+	signal(SIGILL, sigusr1_handler);
+	signal(SIGTERM, sigusr1_handler);
+
+	qb_log_init("bms", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO + verbose);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	if (!use_glib) {
+		bms_loop = qb_loop_create();
+		s1 = qb_ipcs_create("bm1", 0, ipc_type, &sh);
+		if (s1 == 0) {
+			qb_perror(LOG_ERR, "qb_ipcs_create");
+			exit(1);
+		}
+		qb_ipcs_poll_handlers_set(s1, &ph);
+		rc = qb_ipcs_run(s1);
+		if (rc != 0) {
+			errno = -rc;
+			qb_perror(LOG_ERR, "qb_ipcs_run");
+			exit(1);
+		}
+		qb_loop_run(bms_loop);
+	} else {
+#ifdef HAVE_GLIB
+		glib_loop = g_main_loop_new(NULL, FALSE);
+
+		gio_map = qb_array_create(64, sizeof(struct gio_to_qb_poll));
+
+		s1 = qb_ipcs_create("bm1", 0, ipc_type, &sh);
+		if (s1 == 0) {
+			qb_perror(LOG_ERR, "qb_ipcs_create");
+			exit(1);
+		}
+		qb_ipcs_poll_handlers_set(s1, &glib_ph);
+		rc = qb_ipcs_run(s1);
+		if (rc != 0) {
+			errno = -rc;
+			qb_perror(LOG_ERR, "qb_ipcs_run");
+			exit(1);
+		}
+
+		g_main_loop_run(glib_loop);
+#else
+		qb_log(LOG_ERR, "You don't seem to have glib-devel installed.\n");
+#endif
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/tests/check_array.c b/tests/check_array.c
new file mode 100644
index 0000000..38ea3d4
--- /dev/null
+++ b/tests/check_array.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <qb/qbarray.h>
+
+struct test_my_st {
+	int32_t a;
+	int32_t b;
+	int32_t c;
+	int32_t d;
+};
+
+
+START_TEST(test_array_limits)
+{
+	qb_array_t *a;
+	int32_t res;
+	struct test_my_st *st;
+
+	a = qb_array_create(INT_MAX, sizeof(struct test_my_st));
+	fail_unless(a == NULL);
+	a = qb_array_create(-56, sizeof(struct test_my_st));
+	fail_unless(a == NULL);
+	a = qb_array_create(67, 0);
+	fail_unless(a == NULL);
+
+	/* working array */
+	a = qb_array_create(10, sizeof(struct test_my_st));
+	fail_if(a == NULL);
+
+	/* out-of-bounds */
+	res = qb_array_index(a, 10, (void**)&st);
+	ck_assert_int_eq(res, -ERANGE);
+	res = qb_array_index(a, -10, (void**)&st);
+	ck_assert_int_eq(res, -ERANGE);
+	res = qb_array_index(NULL, 1, (void**)&st);
+	ck_assert_int_eq(res, -EINVAL);
+	res = qb_array_index(a, -10, NULL);
+	ck_assert_int_eq(res, -EINVAL);
+
+	qb_array_free(a);
+}
+END_TEST
+
+START_TEST(test_array_alloc_free)
+{
+	qb_array_t *a;
+	a = qb_array_create(65536, sizeof(struct test_my_st));
+	qb_array_free(a);
+}
+END_TEST
+
+START_TEST(test_array_correct_retrieval)
+{
+	qb_array_t *a;
+	int32_t i;
+	int32_t res;
+	struct test_my_st *st;
+
+	a = qb_array_create(112, sizeof(struct test_my_st));
+
+	for (i = 0; i < 112; i++) {
+		res = qb_array_index(a, i, (void**)&st);
+		ck_assert_int_eq(res, 0);
+		st->a = i;
+		st->b = i+1;
+		st->c = i+2;
+		st->d = i+3;
+	}
+	/* read back */
+	for (i = 0; i < 112; i++) {
+		res = qb_array_index(a, i, (void**)&st);
+		ck_assert_int_eq(res, 0);
+		ck_assert_int_eq(st->a, i);
+		ck_assert_int_eq(st->b, i+1);
+		ck_assert_int_eq(st->c, i+2);
+		ck_assert_int_eq(st->d, i+3);
+	}
+
+	qb_array_free(a);
+}
+END_TEST
+
+START_TEST(test_array_static_memory)
+{
+	qb_array_t *a;
+	int32_t res;
+	struct test_my_st *st_old;
+	struct test_my_st *st;
+
+	a = qb_array_create(112, sizeof(struct test_my_st));
+
+	res = qb_array_index(a, 99, (void**)&st_old);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_array_grow(a, 1453);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_array_index(a, 345, (void**)&st);
+	ck_assert_int_eq(res, 0);
+	st->a = 411;
+
+	/* confirm the pointer is the same after a grow */
+	res = qb_array_index(a, 99, (void**)&st);
+	ck_assert_int_eq(res, 0);
+	fail_unless(st == st_old);
+
+	qb_array_free(a);
+}
+END_TEST
+
+static Suite *array_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("qb_array");
+
+	tc = tcase_create("limits");
+	tcase_add_test(tc, test_array_limits);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("alloc_free");
+	tcase_add_test(tc, test_array_alloc_free);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("correct_retrieval");
+	tcase_add_test(tc, test_array_correct_retrieval);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("static_memory");
+	tcase_add_test(tc, test_array_static_memory);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t main(void)
+{
+	int32_t number_failed;
+
+	Suite *s = array_suite();
+	SRunner *sr = srunner_create(s);
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_ipc.c b/tests/check_ipc.c
new file mode 100644
index 0000000..ce9a7c0
--- /dev/null
+++ b/tests/check_ipc.c
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <sys/wait.h>
+#include <signal.h>
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <qb/qbipcc.h>
+#include <qb/qbipcs.h>
+#include <qb/qbloop.h>
+
+static const char *ipc_name = "ipc_test";
+
+#define DEFAULT_MAX_MSG_SIZE (8192*16)
+static int CALCULATED_DGRAM_MAX_MSG_SIZE = 0;
+
+#define DGRAM_MAX_MSG_SIZE \
+	(CALCULATED_DGRAM_MAX_MSG_SIZE == 0 ? \
+	CALCULATED_DGRAM_MAX_MSG_SIZE = qb_ipcc_verify_dgram_max_msg_size(DEFAULT_MAX_MSG_SIZE) : \
+	CALCULATED_DGRAM_MAX_MSG_SIZE)
+
+#define MAX_MSG_SIZE (ipc_type == QB_IPC_SOCKET ? DGRAM_MAX_MSG_SIZE : DEFAULT_MAX_MSG_SIZE)
+
+/* The size the giant msg's data field needs to be to make
+ * this the largests msg we can successfully send. */
+#define GIANT_MSG_DATA_SIZE MAX_MSG_SIZE - sizeof(struct qb_ipc_response_header) - 8
+
+static int enforce_server_buffer=0;
+static qb_ipcc_connection_t *conn;
+static enum qb_ipc_type ipc_type;
+
+enum my_msg_ids {
+	IPC_MSG_REQ_TX_RX,
+	IPC_MSG_RES_TX_RX,
+	IPC_MSG_REQ_DISPATCH,
+	IPC_MSG_RES_DISPATCH,
+	IPC_MSG_REQ_BULK_EVENTS,
+	IPC_MSG_RES_BULK_EVENTS,
+	IPC_MSG_REQ_STRESS_EVENT,
+	IPC_MSG_RES_STRESS_EVENT,
+	IPC_MSG_REQ_SERVER_FAIL,
+	IPC_MSG_RES_SERVER_FAIL,
+	IPC_MSG_REQ_SERVER_DISCONNECT,
+	IPC_MSG_RES_SERVER_DISCONNECT,
+};
+
+/* Test Cases
+ *
+ * 1) basic send & recv differnet message sizes
+ *
+ * 2) send message to start dispatch (confirm receipt)
+ *
+ * 3) flow control
+ *
+ * 4) authentication
+ *
+ * 5) thread safety
+ *
+ * 6) cleanup
+ *
+ * 7) service availabilty
+ *
+ * 8) multiple services
+ */
+static qb_loop_t *my_loop;
+static qb_ipcs_service_t* s1;
+static int32_t turn_on_fc = QB_FALSE;
+static int32_t fc_enabled = 89;
+static int32_t send_event_on_created = QB_FALSE;
+static int32_t disconnect_after_created = QB_FALSE;
+static int32_t num_bulk_events = 10;
+static int32_t num_stress_events = 30000;
+static int32_t reference_count_test = QB_FALSE;
+
+
+static int32_t
+exit_handler(int32_t rsignal, void *data)
+{
+	qb_log(LOG_DEBUG, "caught signal %d", rsignal);
+	qb_ipcs_destroy(s1);
+	return -1;
+}
+
+static int32_t
+s1_msg_process_fn(qb_ipcs_connection_t *c,
+		void *data, size_t size)
+{
+	struct qb_ipc_request_header *req_pt = (struct qb_ipc_request_header *)data;
+	struct qb_ipc_response_header response = { 0, };
+	ssize_t res;
+
+	if (req_pt->id == IPC_MSG_REQ_TX_RX) {
+		response.size = sizeof(struct qb_ipc_response_header);
+		response.id = IPC_MSG_RES_TX_RX;
+		response.error = 0;
+		res = qb_ipcs_response_send(c, &response, response.size);
+		if (res < 0) {
+			qb_perror(LOG_INFO, "qb_ipcs_response_send");
+		} else if (res != response.size) {
+			qb_log(LOG_DEBUG, "qb_ipcs_response_send %zd != %d",
+			       res, response.size);
+		}
+		if (turn_on_fc) {
+			qb_ipcs_request_rate_limit(s1, QB_IPCS_RATE_OFF);
+		}
+	} else if (req_pt->id == IPC_MSG_REQ_DISPATCH) {
+		response.size = sizeof(struct qb_ipc_response_header);
+		response.id = IPC_MSG_RES_DISPATCH;
+		response.error = 0;
+		res = qb_ipcs_event_send(c, &response,
+					 sizeof(response));
+		if (res < 0) {
+			qb_perror(LOG_INFO, "qb_ipcs_event_send");
+		}
+	} else if (req_pt->id == IPC_MSG_REQ_BULK_EVENTS) {
+		int32_t m;
+		int32_t num;
+		struct qb_ipcs_connection_stats_2 *stats;
+		uint32_t max_size = MAX_MSG_SIZE;
+
+		response.size = sizeof(struct qb_ipc_response_header);
+		response.error = 0;
+
+		stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE);
+		num = stats->event_q_length;
+		free(stats);
+
+		/* crazy large message */
+		res = qb_ipcs_event_send(c, &response, max_size*10);
+		ck_assert_int_eq(res, -EMSGSIZE);
+
+		/* send one event before responding */
+		res = qb_ipcs_event_send(c, &response, sizeof(response));
+		ck_assert_int_eq(res, sizeof(response));
+		response.id++;
+
+		/* There should be one more item in the event queue now. */
+		stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE);
+		ck_assert_int_eq(stats->event_q_length - num, 1);
+		free(stats);
+
+		/* send response */
+		response.id = IPC_MSG_RES_BULK_EVENTS;
+		res = qb_ipcs_response_send(c, &response, response.size);
+		ck_assert_int_eq(res, sizeof(response));
+
+		/* send the rest of the events after the response */
+		for (m = 1; m < num_bulk_events; m++) {
+			res = qb_ipcs_event_send(c, &response, sizeof(response));
+
+			if (res == -EAGAIN || res == -ENOBUFS) {
+				/* retry */
+				usleep(1000);
+				m--;
+				continue;
+			}
+			ck_assert_int_eq(res, sizeof(response));
+			response.id++;
+		}
+
+	} else if (req_pt->id == IPC_MSG_REQ_STRESS_EVENT) {
+		struct {
+			struct qb_ipc_response_header hdr __attribute__ ((aligned(8)));
+			char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
+			uint32_t sent_msgs __attribute__ ((aligned(8)));
+		} __attribute__ ((aligned(8))) giant_event_send;
+		int32_t m;
+
+		response.size = sizeof(struct qb_ipc_response_header);
+		response.error = 0;
+
+		response.id = IPC_MSG_RES_STRESS_EVENT;
+		res = qb_ipcs_response_send(c, &response, response.size);
+		ck_assert_int_eq(res, sizeof(response));
+
+		giant_event_send.hdr.error = 0;
+		giant_event_send.hdr.id = IPC_MSG_RES_STRESS_EVENT;
+		for (m = 0; m < num_stress_events; m++) {
+			size_t sent_len = sizeof(struct qb_ipc_response_header);
+
+			if (((m+1) % 1000) == 0) {
+				sent_len = sizeof(giant_event_send);
+				giant_event_send.sent_msgs = m + 1;
+			}
+			giant_event_send.hdr.size = sent_len;
+
+			res = qb_ipcs_event_send(c, &giant_event_send, sent_len);
+			if (res < 0) {
+				if (res == -EAGAIN || res == -ENOBUFS) {
+					/* yield to the receive process */
+					usleep(1000);
+					m--;
+					continue;
+				} else {
+					qb_perror(LOG_DEBUG, "sending stress events");
+					ck_assert_int_eq(res, sent_len);
+				}
+			} else if (((m+1) % 1000) == 0) {
+				qb_log(LOG_DEBUG, "SENT: %d stress events sent", m+1);
+			}
+			giant_event_send.hdr.id++;
+		}
+
+	} else if (req_pt->id == IPC_MSG_REQ_SERVER_FAIL) {
+		exit(0);
+	} else if (req_pt->id == IPC_MSG_REQ_SERVER_DISCONNECT) {
+		qb_ipcs_disconnect(c);
+	}
+	return 0;
+}
+
+static int32_t
+my_job_add(enum qb_loop_priority p,
+			  void *data,
+			  qb_loop_job_dispatch_fn fn)
+{
+	return qb_loop_job_add(my_loop, p, data, fn);
+}
+
+static int32_t
+my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t events,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_add(my_loop, p, fd, events, data, fn);
+}
+
+static int32_t
+my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t events,
+	void *data, qb_ipcs_dispatch_fn_t fn)
+{
+	return qb_loop_poll_mod(my_loop, p, fd, events, data, fn);
+}
+
+static int32_t
+my_dispatch_del(int32_t fd)
+{
+	return qb_loop_poll_del(my_loop, fd);
+}
+
+static int32_t
+s1_connection_closed(qb_ipcs_connection_t *c)
+{
+	qb_enter();
+	qb_leave();
+	return 0;
+}
+
+static void
+outq_flush (void *data)
+{
+	static int i = 0;
+	struct cs_ipcs_conn_context *cnx;
+	cnx = qb_ipcs_context_get(data);
+
+	qb_log(LOG_DEBUG,"iter %u\n", i);
+	i++;
+	if (i == 2) {
+		qb_ipcs_destroy(s1);
+		s1 = NULL;
+	}
+	/* is the reference counting is not working, this should fail
+	 * for i > 1.
+	 */
+	qb_ipcs_event_send(data, "test", 4);
+	assert(memcmp(cnx, "test", 4) == 0);
+	if (i < 5) {
+		qb_loop_job_add(my_loop, QB_LOOP_HIGH, data, outq_flush);
+	} else {
+		/* this single unref should clean everything up.
+		 */
+		qb_ipcs_connection_unref(data);
+		qb_log(LOG_INFO, "end of test, stopping loop");
+		qb_loop_stop(my_loop);
+	}
+}
+
+
+static void
+s1_connection_destroyed(qb_ipcs_connection_t *c)
+{
+	qb_enter();
+	if (reference_count_test) {
+		struct cs_ipcs_conn_context *cnx;
+		cnx = qb_ipcs_context_get(c);
+		free(cnx);
+	} else {
+		qb_loop_stop(my_loop);
+	}
+	qb_leave();
+}
+
+static void
+s1_connection_created(qb_ipcs_connection_t *c)
+{
+	uint32_t max = MAX_MSG_SIZE;
+
+	if (send_event_on_created) {
+		struct qb_ipc_response_header response;
+		int32_t res;
+
+		response.size = sizeof(struct qb_ipc_response_header);
+		response.id = IPC_MSG_RES_DISPATCH;
+		response.error = 0;
+		res = qb_ipcs_event_send(c, &response,
+					 sizeof(response));
+		ck_assert_int_eq(res, response.size);
+	}
+	if (reference_count_test) {
+		struct cs_ipcs_conn_context *context;
+
+		qb_ipcs_connection_ref(c);
+		qb_loop_job_add(my_loop, QB_LOOP_HIGH, c, outq_flush);
+
+		context = calloc(1, 20);
+		memcpy(context, "test", 4);
+		qb_ipcs_context_set(c, context);
+	}
+
+
+	ck_assert_int_eq(max, qb_ipcs_connection_get_buffer_size(c));
+
+}
+
+static void
+run_ipc_server(void)
+{
+	int32_t res;
+	qb_loop_signal_handle handle;
+
+	struct qb_ipcs_service_handlers sh = {
+		.connection_accept = NULL,
+		.connection_created = s1_connection_created,
+		.msg_process = s1_msg_process_fn,
+		.connection_destroyed = s1_connection_destroyed,
+		.connection_closed = s1_connection_closed,
+	};
+
+	struct qb_ipcs_poll_handlers ph = {
+		.job_add = my_job_add,
+		.dispatch_add = my_dispatch_add,
+		.dispatch_mod = my_dispatch_mod,
+		.dispatch_del = my_dispatch_del,
+	};
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	qb_loop_signal_add(my_loop, QB_LOOP_HIGH, SIGSTOP,
+			   NULL, exit_handler, &handle);
+	qb_loop_signal_add(my_loop, QB_LOOP_HIGH, SIGTERM,
+			   NULL, exit_handler, &handle);
+
+	my_loop = qb_loop_create();
+
+	s1 = qb_ipcs_create(ipc_name, 4, ipc_type, &sh);
+	fail_if(s1 == 0);
+
+	if (enforce_server_buffer) {
+		qb_ipcs_enforce_buffer_size(s1, max_size);
+	}
+	qb_ipcs_poll_handlers_set(s1, &ph);
+
+	res = qb_ipcs_run(s1);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(my_loop);
+	qb_log(LOG_DEBUG, "loop finished - done ...");
+}
+
+static int32_t
+run_function_in_new_process(void (*run_ipc_server_fn)(void))
+{
+	pid_t pid = fork ();
+
+	if (pid == -1) {
+		fprintf (stderr, "Can't fork\n");
+		return -1;
+	}
+
+	if (pid == 0) {
+		run_ipc_server_fn();
+		return 0;
+	}
+	return pid;
+}
+
+static void
+request_server_exit(void)
+{
+	struct qb_ipc_request_header req_header;
+	struct qb_ipc_response_header res_header;
+	struct iovec iov[1];
+	int32_t res;
+
+	/*
+	 * tell the server to exit
+	 */
+	req_header.id = IPC_MSG_REQ_SERVER_FAIL;
+	req_header.size = sizeof(struct qb_ipc_request_header);
+
+	iov[0].iov_len = req_header.size;
+	iov[0].iov_base = &req_header;
+
+	ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn));
+
+	res = qb_ipcc_sendv_recv(conn, iov, 1,
+				 &res_header,
+				 sizeof(struct qb_ipc_response_header), -1);
+	/*
+	 * confirm we get -ENOTCONN or ECONNRESET
+	 */
+	if (res != -ECONNRESET && res != -ENOTCONN) {
+		qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
+		ck_assert_int_eq(res, -ENOTCONN);
+	}
+}
+
+static void
+kill_server(pid_t pid)
+{
+	kill(pid, SIGTERM);
+	waitpid(pid, NULL, 0);
+}
+
+static int32_t
+verify_graceful_stop(pid_t pid)
+{
+	int wait_rc = 0;
+	int status = 0;
+	int rc = 0;
+	int tries;
+
+	/* We need the server to be able to exit by itself */
+	for (tries = 10;  tries >= 0; tries--) {
+		sleep(1);
+		wait_rc = waitpid(pid, &status, WNOHANG);
+		if (wait_rc > 0) {
+			break;
+		}
+	}
+
+	ck_assert_int_eq(wait_rc, pid);
+	rc = WIFEXITED(status);
+	if (rc) {
+		rc = WEXITSTATUS(status);
+		ck_assert_int_eq(rc, 0);
+	} else {
+		fail_if(rc == 0);
+	}
+	
+	return 0;
+}
+
+struct my_req {
+	struct qb_ipc_request_header hdr;
+	char message[1024 * 1024];
+};
+
+static struct my_req request;
+static int32_t
+send_and_check(int32_t req_id, uint32_t size,
+	       int32_t ms_timeout, int32_t expect_perfection)
+{
+	struct qb_ipc_response_header res_header;
+	int32_t res;
+	int32_t try_times = 0;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	request.hdr.id = req_id;
+	request.hdr.size = sizeof(struct qb_ipc_request_header) + size;
+
+	/* check that we can't send a message that is too big
+	 * and we get the right return code.
+	 */
+	res = qb_ipcc_send(conn, &request, max_size*2);
+	ck_assert_int_eq(res, -EMSGSIZE);
+
+repeat_send:
+	res = qb_ipcc_send(conn, &request, request.hdr.size);
+	try_times++;
+	if (res < 0) {
+		if (res == -EAGAIN && try_times < 10) {
+			goto repeat_send;
+		} else {
+			if (res == -EAGAIN && try_times >= 10) {
+				fc_enabled = QB_TRUE;
+			}
+			errno = -res;
+			qb_perror(LOG_INFO, "qb_ipcc_send");
+			return res;
+		}
+	}
+
+	if (req_id == IPC_MSG_REQ_DISPATCH) {
+		res = qb_ipcc_event_recv(conn, &res_header,
+					 sizeof(struct qb_ipc_response_header),
+					 ms_timeout);
+	} else {
+		res = qb_ipcc_recv(conn, &res_header,
+				   sizeof(struct qb_ipc_response_header),
+				   ms_timeout);
+	}
+	if (res == -EINTR) {
+		return -1;
+	}
+	if (res == -EAGAIN || res == -ETIMEDOUT) {
+		fc_enabled = QB_TRUE;
+		qb_perror(LOG_DEBUG, "qb_ipcc_recv");
+		return res;
+	}
+	if (expect_perfection) {
+		ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
+		ck_assert_int_eq(res_header.id, req_id + 1);
+		ck_assert_int_eq(res_header.size, sizeof(struct qb_ipc_response_header));
+	}
+	return res;
+}
+
+static void
+test_ipc_txrx_timeout(void)
+{
+	struct qb_ipc_request_header req_header;
+	struct qb_ipc_response_header res_header;
+	struct iovec iov[1];
+	int32_t res;
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	/* The dispatch response will only come over
+	 * the event channel, we want to verify the receive times
+	 * out when an event is returned with no response */
+	req_header.id = IPC_MSG_REQ_DISPATCH;
+	req_header.size = sizeof(struct qb_ipc_request_header);
+
+	iov[0].iov_len = req_header.size;
+	iov[0].iov_base = &req_header;
+
+	res = qb_ipcc_sendv_recv(conn, iov, 1,
+				 &res_header,
+				 sizeof(struct qb_ipc_response_header), 5000);
+
+	ck_assert_int_eq(res, -ETIMEDOUT);
+
+	request_server_exit();
+	verify_graceful_stop(pid);
+
+	/*
+	 * wait a bit for the server to die.
+	 */
+	sleep(1);
+
+	/*
+	 * this needs to free up the shared mem
+	 */
+	qb_ipcc_disconnect(conn);
+}
+
+static int32_t recv_timeout = -1;
+static void
+test_ipc_txrx(void)
+{
+	int32_t j;
+	int32_t c = 0;
+	size_t size;
+	pid_t pid;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	size = QB_MIN(sizeof(struct qb_ipc_request_header), 64);
+	for (j = 1; j < 19; j++) {
+		size *= 2;
+		if (size >= max_size)
+			break;
+		if (send_and_check(IPC_MSG_REQ_TX_RX, size,
+				   recv_timeout, QB_TRUE) < 0) {
+			break;
+		}
+	}
+	if (turn_on_fc) {
+		/* can't signal server to shutdown if flow control is on */
+		ck_assert_int_eq(fc_enabled, QB_TRUE);
+		qb_ipcc_disconnect(conn);
+		/* TODO - figure out why this sleep is necessary */
+		sleep(1);
+		kill_server(pid);
+	} else {
+		request_server_exit();
+		qb_ipcc_disconnect(conn);
+		verify_graceful_stop(pid);
+	}
+}
+
+static void
+test_ipc_exit(void)
+{
+	struct qb_ipc_request_header req_header;
+	struct qb_ipc_response_header res_header;
+	struct iovec iov[1];
+	int32_t res;
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	req_header.id = IPC_MSG_REQ_TX_RX;
+	req_header.size = sizeof(struct qb_ipc_request_header);
+
+	iov[0].iov_len = req_header.size;
+	iov[0].iov_base = &req_header;
+
+	res = qb_ipcc_sendv_recv(conn, iov, 1,
+				 &res_header,
+				 sizeof(struct qb_ipc_response_header), -1);
+	ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
+
+	request_server_exit();
+	verify_graceful_stop(pid);
+
+	/*
+	 * wait a bit for the server to die.
+	 */
+	sleep(1);
+
+	/*
+	 * this needs to free up the shared mem
+	 */
+	qb_ipcc_disconnect(conn);
+}
+
+START_TEST(test_ipc_exit_us)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	recv_timeout = 5000;
+	test_ipc_exit();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_exit_shm)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	recv_timeout = 1000;
+	test_ipc_exit();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_shm_timeout)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_txrx_timeout();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_us_timeout)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_txrx_timeout();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_shm_tmo)
+{
+	qb_enter();
+	turn_on_fc = QB_FALSE;
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	recv_timeout = 1000;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_shm_block)
+{
+	qb_enter();
+	turn_on_fc = QB_FALSE;
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	recv_timeout = -1;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_fc_shm)
+{
+	qb_enter();
+	turn_on_fc = QB_TRUE;
+	ipc_type = QB_IPC_SHM;
+	recv_timeout = 500;
+	ipc_name = __func__;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_us_block)
+{
+	qb_enter();
+	turn_on_fc = QB_FALSE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	recv_timeout = -1;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_txrx_us_tmo)
+{
+	qb_enter();
+	turn_on_fc = QB_FALSE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	recv_timeout = 1000;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_fc_us)
+{
+	qb_enter();
+	turn_on_fc = QB_TRUE;
+	ipc_type = QB_IPC_SOCKET;
+	recv_timeout = 500;
+	ipc_name = __func__;
+	test_ipc_txrx();
+	qb_leave();
+}
+END_TEST
+
+struct my_res {
+	struct qb_ipc_response_header hdr;
+	char message[1024 * 1024];
+};
+
+static void
+test_ipc_dispatch(void)
+{
+	int32_t j;
+	int32_t c = 0;
+	pid_t pid;
+	int32_t size;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	size = QB_MIN(sizeof(struct qb_ipc_request_header), 64);
+	for (j = 1; j < 19; j++) {
+		size *= 2;
+		if (size >= max_size)
+			break;
+		if (send_and_check(IPC_MSG_REQ_DISPATCH, size,
+				   recv_timeout, QB_TRUE) < 0) {
+			break;
+		}
+	}
+
+	request_server_exit();
+	qb_ipcc_disconnect(conn);
+	verify_graceful_stop(pid);
+}
+
+START_TEST(test_ipc_disp_us)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_dispatch();
+	qb_leave();
+}
+END_TEST
+
+static int32_t events_received;
+
+static int32_t
+count_stress_events(int32_t fd, int32_t revents, void *data)
+{
+	struct {
+		struct qb_ipc_response_header hdr __attribute__ ((aligned(8)));
+		char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
+		uint32_t sent_msgs __attribute__ ((aligned(8)));
+	} __attribute__ ((aligned(8))) giant_event_recv;
+	qb_loop_t *cl = (qb_loop_t*)data;
+	int32_t res;
+
+	res = qb_ipcc_event_recv(conn, &giant_event_recv,
+				 sizeof(giant_event_recv),
+				 -1);
+	if (res > 0) {
+		events_received++;
+
+		if ((events_received % 1000) == 0) {
+			qb_log(LOG_DEBUG, "RECV: %d stress events processed", events_received);
+			if (res != sizeof(giant_event_recv)) {
+				qb_log(LOG_DEBUG, "Unexpected recv size, expected %d got %d",
+					res, sizeof(giant_event_recv));
+
+				ck_assert_int_eq(res, sizeof(giant_event_recv));
+			} else if (giant_event_recv.sent_msgs != events_received) {
+				qb_log(LOG_DEBUG, "Server event mismatch. Server thinks we got %d msgs, but we only received %d",
+					giant_event_recv.sent_msgs, events_received);
+				/* This indicates that data corruption is occurring. Since the events
+				 * received is placed at the end of the giant msg, it is possible
+				 * that buffers were not allocated correctly resulting in us
+				 * reading/writing to uninitialized memeory at some point. */
+				ck_assert_int_eq(giant_event_recv.sent_msgs, events_received);
+			}
+		}
+	} else if (res != -EAGAIN) {
+		qb_perror(LOG_DEBUG, "count_stress_events");
+		qb_loop_stop(cl);
+		return -1;
+	}
+
+	if (events_received >= num_stress_events) {
+		qb_loop_stop(cl);
+		return -1;
+	}
+	return 0;
+}
+
+static int32_t
+count_bulk_events(int32_t fd, int32_t revents, void *data)
+{
+	qb_loop_t *cl = (qb_loop_t*)data;
+	struct qb_ipc_response_header res_header;
+	int32_t res;
+
+	res = qb_ipcc_event_recv(conn, &res_header,
+				 sizeof(struct qb_ipc_response_header),
+				 -1);
+	if (res > 0) {
+		events_received++;
+	}
+
+	if (events_received >= num_bulk_events) {
+		qb_loop_stop(cl);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+test_ipc_bulk_events(void)
+{
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	int32_t res;
+	qb_loop_t *cl;
+	int32_t fd;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	events_received = 0;
+	cl = qb_loop_create();
+	res = qb_ipcc_fd_get(conn, &fd);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_poll_add(cl, QB_LOOP_MED,
+			 fd, POLLIN,
+			 cl, count_bulk_events);
+	ck_assert_int_eq(res, 0);
+
+	res = send_and_check(IPC_MSG_REQ_BULK_EVENTS,
+			     0,
+			     recv_timeout, QB_TRUE);
+	ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
+
+	qb_loop_run(cl);
+	ck_assert_int_eq(events_received, num_bulk_events);
+
+	request_server_exit();
+	qb_ipcc_disconnect(conn);
+	verify_graceful_stop(pid);
+}
+
+static void
+test_ipc_stress_test(void)
+{
+	struct {
+		struct qb_ipc_request_header hdr __attribute__ ((aligned(8)));
+		char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
+		uint32_t sent_msgs __attribute__ ((aligned(8)));
+	} __attribute__ ((aligned(8))) giant_req;
+
+	struct qb_ipc_response_header res_header;
+	struct iovec iov[1];
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	int32_t res;
+	qb_loop_t *cl;
+	int32_t fd;
+	uint32_t max_size = MAX_MSG_SIZE;
+	/* This looks strange, but it serves an important purpose.
+	 * This test forces the server to enforce the MAX_MSG_SIZE
+	 * limit from the server side, which overrides the client's
+	 * buffer limit.  To verify this functionality is working
+	 * we set the client limit lower than what the server
+	 * is enforcing. */
+	int32_t client_buf_size = max_size - 1024;
+	int32_t real_buf_size;
+
+	enforce_server_buffer = 1;
+	pid = run_function_in_new_process(run_ipc_server);
+	enforce_server_buffer = 0;
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, client_buf_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	real_buf_size = qb_ipcc_get_buffer_size(conn);
+	ck_assert_int_eq(real_buf_size, max_size);
+
+	qb_log(LOG_DEBUG, "Testing %d iterations of EVENT msg passing.", num_stress_events);
+
+	events_received = 0;
+	cl = qb_loop_create();
+	res = qb_ipcc_fd_get(conn, &fd);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_poll_add(cl, QB_LOOP_MED,
+			 fd, POLLIN,
+			 cl, count_stress_events);
+	ck_assert_int_eq(res, 0);
+
+	res = send_and_check(IPC_MSG_REQ_STRESS_EVENT, 0, recv_timeout, QB_TRUE);
+
+	qb_loop_run(cl);
+	ck_assert_int_eq(events_received, num_stress_events);
+
+	giant_req.hdr.id = IPC_MSG_REQ_SERVER_FAIL;
+	giant_req.hdr.size = sizeof(giant_req);
+
+	if (giant_req.hdr.size <= client_buf_size) {
+		ck_assert_int_eq(1, 0);
+	}
+
+	iov[0].iov_len = giant_req.hdr.size;
+	iov[0].iov_base = &giant_req;
+	res = qb_ipcc_sendv_recv(conn, iov, 1,
+				 &res_header,
+				 sizeof(struct qb_ipc_response_header), -1);
+	if (res != -ECONNRESET && res != -ENOTCONN) {
+		qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
+		ck_assert_int_eq(res, -ENOTCONN);
+	}
+
+	qb_ipcc_disconnect(conn);
+	verify_graceful_stop(pid);
+}
+
+START_TEST(test_ipc_stress_test_us)
+{
+	qb_enter();
+	send_event_on_created = QB_FALSE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_stress_test();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_bulk_events_us)
+{
+	qb_enter();
+	send_event_on_created = QB_FALSE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_bulk_events();
+	qb_leave();
+}
+END_TEST
+
+static void
+test_ipc_event_on_created(void)
+{
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	int32_t res;
+	qb_loop_t *cl;
+	int32_t fd;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	num_bulk_events = 1;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	events_received = 0;
+	cl = qb_loop_create();
+	res = qb_ipcc_fd_get(conn, &fd);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_poll_add(cl, QB_LOOP_MED,
+			 fd, POLLIN,
+			 cl, count_bulk_events);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(cl);
+	ck_assert_int_eq(events_received, num_bulk_events);
+
+	request_server_exit();
+	qb_ipcc_disconnect(conn);
+	verify_graceful_stop(pid);
+}
+
+START_TEST(test_ipc_event_on_created_us)
+{
+	qb_enter();
+	send_event_on_created = QB_TRUE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_event_on_created();
+	qb_leave();
+}
+END_TEST
+
+static void
+test_ipc_disconnect_after_created(void)
+{
+	struct qb_ipc_request_header req_header;
+	struct qb_ipc_response_header res_header;
+	struct iovec iov[1];
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	int32_t res;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn));
+
+	req_header.id = IPC_MSG_REQ_SERVER_DISCONNECT;
+	req_header.size = sizeof(struct qb_ipc_request_header);
+
+	iov[0].iov_len = req_header.size;
+	iov[0].iov_base = &req_header;
+
+	res = qb_ipcc_sendv_recv(conn, iov, 1,
+				 &res_header,
+				 sizeof(struct qb_ipc_response_header), -1);
+	/*
+	 * confirm we get -ENOTCONN or -ECONNRESET
+	 */
+	if (res != -ECONNRESET && res != -ENOTCONN) {
+		qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
+		ck_assert_int_eq(res, -ENOTCONN);
+	}
+	ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn));
+
+	qb_ipcc_disconnect(conn);
+	kill_server(pid);
+}
+
+START_TEST(test_ipc_disconnect_after_created_us)
+{
+	qb_enter();
+	disconnect_after_created = QB_TRUE;
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_disconnect_after_created();
+	qb_leave();
+}
+END_TEST
+
+static void
+test_ipc_server_fail(void)
+{
+	int32_t j;
+	int32_t c = 0;
+	pid_t pid;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	request_server_exit();
+	ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn));
+	qb_ipcc_disconnect(conn);
+	verify_graceful_stop(pid);
+}
+
+START_TEST(test_ipc_server_fail_soc)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_server_fail();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_disp_shm)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_dispatch();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_stress_test_shm)
+{
+	qb_enter();
+	send_event_on_created = QB_FALSE;
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_stress_test();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_bulk_events_shm)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_bulk_events();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_event_on_created_shm)
+{
+	qb_enter();
+	send_event_on_created = QB_TRUE;
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_event_on_created();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_server_fail_shm)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_server_fail();
+	qb_leave();
+}
+END_TEST
+
+static void
+test_ipc_service_ref_count(void)
+{
+	int32_t c = 0;
+	int32_t j = 0;
+	pid_t pid;
+	uint32_t max_size = MAX_MSG_SIZE;
+
+	reference_count_test = QB_TRUE;
+
+	pid = run_function_in_new_process(run_ipc_server);
+	fail_if(pid == -1);
+	sleep(1);
+
+	do {
+		conn = qb_ipcc_connect(ipc_name, max_size);
+		if (conn == NULL) {
+			j = waitpid(pid, NULL, WNOHANG);
+			ck_assert_int_eq(j, 0);
+			sleep(1);
+			c++;
+		}
+	} while (conn == NULL && c < 5);
+	fail_if(conn == NULL);
+
+	sleep(5);
+
+	kill_server(pid);
+}
+
+
+START_TEST(test_ipc_service_ref_count_shm)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SHM;
+	ipc_name = __func__;
+	test_ipc_service_ref_count();
+	qb_leave();
+}
+END_TEST
+
+START_TEST(test_ipc_service_ref_count_us)
+{
+	qb_enter();
+	ipc_type = QB_IPC_SOCKET;
+	ipc_name = __func__;
+	test_ipc_service_ref_count();
+	qb_leave();
+}
+END_TEST
+
+static void test_max_dgram_size(void)
+{
+	/* most implementations will not let you set a dgram buffer 
+	 * of 1 million bytes. This test verifies that the we can detect
+	 * the max dgram buffersize regardless, and that the value we detect
+	 * is consistent. */
+	int32_t init;
+	int32_t i;
+
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_REMOVE,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+
+	init = qb_ipcc_verify_dgram_max_msg_size(1000000);
+	fail_if(init <= 0);
+	for (i = 0; i < 100; i++) {
+		int try = qb_ipcc_verify_dgram_max_msg_size(1000000);
+		ck_assert_int_eq(init, try);
+	}
+
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+}
+
+START_TEST(test_ipc_max_dgram_size)
+{
+	qb_enter();
+	test_max_dgram_size();
+	qb_leave();
+}
+END_TEST
+
+static Suite *
+make_shm_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("shm");
+
+	tc = tcase_create("ipc_txrx_shm_timeout");
+	tcase_add_test(tc, test_ipc_txrx_shm_timeout);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_server_fail_shm");
+	tcase_add_test(tc, test_ipc_server_fail_shm);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_txrx_shm_block");
+	tcase_add_test(tc, test_ipc_txrx_shm_block);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_txrx_shm_tmo");
+	tcase_add_test(tc, test_ipc_txrx_shm_tmo);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_fc_shm");
+	tcase_add_test(tc, test_ipc_fc_shm);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_dispatch_shm");
+	tcase_add_test(tc, test_ipc_disp_shm);
+	tcase_set_timeout(tc, 16);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_stress_test_shm");
+	tcase_add_test(tc, test_ipc_stress_test_shm);
+	tcase_set_timeout(tc, 16);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_bulk_events_shm");
+	tcase_add_test(tc, test_ipc_bulk_events_shm);
+	tcase_set_timeout(tc, 16);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_exit_shm");
+	tcase_add_test(tc, test_ipc_exit_shm);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_event_on_created_shm");
+	tcase_add_test(tc, test_ipc_event_on_created_shm);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_service_ref_count_shm");
+	tcase_add_test(tc, test_ipc_service_ref_count_shm);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+static Suite *
+make_soc_suite(void)
+{
+	Suite *s = suite_create("socket");
+	TCase *tc;
+
+	tc = tcase_create("ipc_txrx_us_timeout");
+	tcase_add_test(tc, test_ipc_txrx_us_timeout);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_max_dgram_size");
+	tcase_add_test(tc, test_ipc_max_dgram_size);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_server_fail_soc");
+	tcase_add_test(tc, test_ipc_server_fail_soc);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_txrx_us_block");
+	tcase_add_test(tc, test_ipc_txrx_us_block);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_txrx_us_tmo");
+	tcase_add_test(tc, test_ipc_txrx_us_tmo);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_fc_us");
+	tcase_add_test(tc, test_ipc_fc_us);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_exit_us");
+	tcase_add_test(tc, test_ipc_exit_us);
+	tcase_set_timeout(tc, 8);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_dispatch_us");
+	tcase_add_test(tc, test_ipc_disp_us);
+	tcase_set_timeout(tc, 16);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_stress_test_us");
+	tcase_add_test(tc, test_ipc_stress_test_us);
+	tcase_set_timeout(tc, 60);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_bulk_events_us");
+	tcase_add_test(tc, test_ipc_bulk_events_us);
+	tcase_set_timeout(tc, 16);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_event_on_created_us");
+	tcase_add_test(tc, test_ipc_event_on_created_us);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_disconnect_after_created_us");
+	tcase_add_test(tc, test_ipc_disconnect_after_created_us);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("ipc_service_ref_count_us");
+	tcase_add_test(tc, test_ipc_service_ref_count_us);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t
+main(void)
+{
+	int32_t number_failed;
+	SRunner *sr;
+	Suite *s;
+	int32_t do_shm_tests = QB_TRUE;
+
+#ifdef DISABLE_IPC_SHM
+	do_shm_tests = QB_FALSE;
+#endif /* DISABLE_IPC_SHM */
+
+	s = make_soc_suite();
+	sr = srunner_create(s);
+
+	if (do_shm_tests) {
+		srunner_add_suite(sr, make_shm_suite());
+	}
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+	qb_log_format_set(QB_LOG_STDERR, "lib/%f|%l| %b");
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_log.c b/tests/check_log.c
new file mode 100644
index 0000000..22eb0a5
--- /dev/null
+++ b/tests/check_log.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <pthread.h>
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap);
+extern size_t qb_vsnprintf_deserialize(char *string, size_t strlen, const char *buf);
+
+
+static void
+format_this(char *out, const char *fmt, ...)
+{
+	char buf[QB_LOG_MAX_LEN];
+	va_list ap;
+
+	va_start(ap, fmt);
+	qb_vsnprintf_serialize(buf, QB_LOG_MAX_LEN, fmt, ap);
+	qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf);
+	va_end(ap);
+}
+
+static void
+format_this_up_to(char *out, size_t max_len, const char *fmt, ...)
+{
+	char buf[QB_LOG_MAX_LEN];
+	va_list ap;
+
+	va_start(ap, fmt);
+	qb_vsnprintf_serialize(buf, max_len, fmt, ap);
+	qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf);
+	va_end(ap);
+}
+
+START_TEST(test_va_serialize)
+{
+	char buf[QB_LOG_MAX_LEN];
+	char cmp_buf[QB_LOG_MAX_LEN];
+
+	format_this(buf, "one line");
+	ck_assert_str_eq(buf, "one line");
+
+	format_this(buf, "p1:%p, p2:%p", format_this, buf);
+	snprintf(cmp_buf, QB_LOG_MAX_LEN, "p1:%p, p2:%p", format_this, buf);
+	ck_assert_str_eq(buf, cmp_buf);
+
+	format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
+	ck_assert_str_eq(buf, "s1:Yes, s2:Never");
+
+	format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
+	ck_assert_str_eq(buf, "s1:Yes, s2:Never");
+
+	format_this(buf, "d1:%d, d2:%5i, d3:%04i", 23, 37, 84);
+	ck_assert_str_eq(buf, "d1:23, d2:   37, d3:0084");
+
+	format_this(buf, "f1:%.5f, f2:%.2f", 23.34109, 23.34109);
+	ck_assert_str_eq(buf, "f1:23.34109, f2:23.34");
+
+	format_this(buf, ":%s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, world!:");
+	format_this(buf, ":%15s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":  Hello, world!:");
+	format_this(buf, ":%.10s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, wor:");
+	format_this(buf, ":%-10s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, world!:");
+	format_this(buf, ":%-15s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, world!  :");
+	format_this(buf, ":%.15s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, world!:");
+	format_this(buf, ":%15.10s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":     Hello, wor:");
+	format_this(buf, ":%-15.10s:", "Hello, world!");
+	ck_assert_str_eq(buf, ":Hello, wor     :");
+
+	format_this(buf, ":%*d:", 8, 96);
+	ck_assert_str_eq(buf, ":      96:");
+
+	format_this_up_to(buf, 11, "123456789____");
+	ck_assert_str_eq(buf, "123456789_");
+
+	format_this(buf, "Client %s.%.9s wants to fence (%s) '%s' with device '%s'",
+		    "bla", "foooooooooooooooooo",
+		    "action", "target", "hoop");
+
+	ck_assert_str_eq(buf,
+			 "Client bla.foooooooo wants to fence (action) 'target' with device 'hoop'");
+
+	format_this(buf, "Node %s now has process list: %.32x (was %.32x)",
+		    "18builder", 2, 0);
+	ck_assert_str_eq(buf, "Node 18builder now has process list: 00000000000000000000000000000002 (was 00000000000000000000000000000000)");
+
+
+}
+END_TEST
+
+START_TEST(test_log_stupid_inputs)
+{
+	int32_t rc;
+
+	/* shouldn't crash with out an init() */
+	qb_log_fini();
+
+	/* not init'ed */
+	rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
+			       QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
+	ck_assert_int_eq(rc, -EINVAL);
+
+	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 2000);
+	ck_assert_int_eq(rc, -EINVAL);
+
+	qb_log(LOG_INFO, "not init'd");
+
+	qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+				    __LINE__, 0, "also not init'd");
+
+	qb_log_init("test", LOG_USER, LOG_DEBUG);
+
+	/* non-opened log file */
+	rc = qb_log_filter_ctl(21, QB_LOG_FILTER_ADD,
+			       QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
+	ck_assert_int_eq(rc, -EBADF);
+
+	rc = qb_log_ctl(21, QB_LOG_CONF_PRIORITY_BUMP, -1);
+	ck_assert_int_eq(rc, -EBADF);
+
+	/* target < 0 or >= 32 */
+	rc = qb_log_filter_ctl(41, QB_LOG_FILTER_ADD,
+			       QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
+	ck_assert_int_eq(rc, -EBADF);
+
+	rc = qb_log_ctl(-1, QB_LOG_CONF_PRIORITY_BUMP, -1);
+	ck_assert_int_eq(rc, -EBADF);
+
+	/* crap values to filter_ctl() */
+	rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
+			       QB_LOG_FILTER_FILE, NULL, LOG_INFO);
+	ck_assert_int_eq(rc, -EINVAL);
+	rc = qb_log_filter_ctl(QB_LOG_SYSLOG, 56,
+			       QB_LOG_FILTER_FILE, "boja", LOG_INFO);
+	ck_assert_int_eq(rc, -EINVAL);
+
+	/* crap values to ctl() */
+	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, -2000);
+	ck_assert_int_eq(rc, -EINVAL);
+	rc = qb_log_ctl(QB_LOG_BLACKBOX, 67, 2000);
+	ck_assert_int_eq(rc, -EINVAL);
+	rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_SIZE, 2000);
+	ck_assert_int_eq(rc, -ENOSYS);
+
+}
+END_TEST
+
+static char test_buf[QB_LOG_MAX_LEN];
+static uint8_t test_priority;
+static int32_t num_msgs;
+
+/*
+ * to test that we get what we expect.
+ */
+static void
+_test_logger(int32_t t,
+	     struct qb_log_callsite *cs,
+	     time_t timestamp, const char *msg)
+{
+	test_buf[0] = '\0';
+	qb_log_target_format(t, cs, timestamp, msg, test_buf);
+	test_priority = cs->priority;
+
+	num_msgs++;
+}
+
+static void log_also(void)
+{
+	qb_log(LOG_INFO, "yes please");
+}
+
+static void log_and_this_too(void)
+{
+	qb_log(LOG_INFO, "this too please");
+}
+
+static void log_it_please(void)
+{
+	qb_enter();
+	qb_log(LOG_TRACE, "A:%d B:%d C:%d", 1, 2, 3);
+	qb_log(LOG_DEBUG, "A:%d B:%d C:%d", 1, 2, 3);
+	errno = EEXIST;
+	qb_perror(LOG_WARNING, "bogus error");
+	errno = 0;
+	qb_log(LOG_INFO, "A:%d B:%d C:%d", 1, 2, 3);
+	qb_log(LOG_NOTICE, "A:%d B:%d C:%d", 1, 2, 3);
+	qb_log(LOG_WARNING, "A:%d B:%d C:%d", 1, 2, 3);
+	qb_log(LOG_ERR, "A:%d B:%d C:%d", 1, 2, 3);
+	qb_leave();
+}
+
+
+static int32_t _cust_t = -1;
+static void
+m_filter(struct qb_log_callsite *cs)
+{
+	if ((cs->priority >= LOG_ALERT &&
+	     cs->priority <= LOG_INFO) ||
+	    cs->tags > 0) {
+		qb_bit_set(cs->targets, _cust_t);
+	} else {
+		qb_bit_clear(cs->targets, _cust_t);
+	}
+}
+
+
+START_TEST(test_log_filter_fn)
+{
+	int32_t rc;
+
+	qb_log_init("test", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+	_cust_t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
+	_ck_assert_int(_cust_t, >, QB_LOG_BLACKBOX);
+	rc = qb_log_ctl(_cust_t, QB_LOG_CONF_ENABLED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+
+	/*
+	 * test the custom filter function.
+	 * make sure qb_log, and qb_log_from_external_source are filtered.
+	 */
+	qb_log_filter_fn_set(m_filter);
+	num_msgs = 0;
+
+	qb_log(LOG_NOTICE, "qb_log_filter_fn_set good");
+	qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+				    __LINE__, 0, "qb_log_filter_fn_set good");
+	qb_log(LOG_TRACE, "qb_log_filter_fn_set bad");
+	qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
+				    __LINE__, 44, "qb_log_filter_fn_set woot");
+	qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
+				    __LINE__, 0, "qb_log_filter_fn_set bad");
+
+	ck_assert_int_eq(num_msgs, 3);
+}
+END_TEST
+
+START_TEST(test_log_basic)
+{
+	int32_t t;
+	int32_t rc;
+
+	qb_log_init("test", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+	t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
+	rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FORMAT, "Angus", LOG_WARNING);
+	ck_assert_int_eq(rc, 0);
+	qb_log_format_set(t, "%b");
+	rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+
+	/* captures last log */
+	memset(test_buf, 0, sizeof(test_buf));
+	test_priority = 0;
+	num_msgs = 0;
+
+	/*
+	 * test filtering by format
+	 */
+	qb_log(LOG_INFO, "Hello Angus, how are you?");
+	qb_log(LOG_WARNING, "Hello Steven, how are you?");
+	qb_log(LOG_ERR, "Hello Andrew, how are you?");
+	qb_log(LOG_ERR, "Hello Angus, how are you?");
+	qb_log(LOG_EMERG, "Hello Anna, how are you?");
+	ck_assert_int_eq(test_priority, LOG_ERR);
+	ck_assert_int_eq(num_msgs, 1);
+	ck_assert_str_eq(test_buf, "Hello Angus, how are you?");
+
+
+	/* 
+	 * test filtering by file regex
+ 	 */
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FORMAT, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE_REGEX, "^fakefile*", LOG_DEBUG);
+
+	num_msgs = 0;
+	qb_log_from_external_source(__func__, "fakefile_logging", "%s bla", LOG_INFO,
+				    56, 0, "filename/lineno");
+	qb_log_from_external_source(__func__, "do_not_log_fakefile_logging", "%s bla", LOG_INFO,
+				    56, 0, "filename/lineno");
+	ck_assert_int_eq(num_msgs, 1);
+
+	/* 
+	 * test filtering by format regex
+ 	 */
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FORMAT, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FORMAT_REGEX, "^one", LOG_WARNING);
+
+	num_msgs = 0;
+	qb_log(LOG_INFO, "one two three");
+	qb_log(LOG_ERR, "testing one two three");
+	qb_log(LOG_WARNING, "one two three");
+	qb_log(LOG_ERR, "one two three");
+	qb_log(LOG_EMERG, "one two three");
+	ck_assert_int_eq(num_msgs, 3);
+
+	/*
+	 * test filtering by function and regex
+	 */
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FUNCTION_REGEX, "^log_.*please", LOG_WARNING);
+
+	num_msgs = 0;
+	qb_log(LOG_ERR, "try if you: log_it_please()");
+	log_it_please();
+	ck_assert_int_eq(num_msgs, 3);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE,
+			  QB_LOG_FILTER_FUNCTION_REGEX, "log_it_please", LOG_WARNING);
+
+	/*
+	 * test filtering by function
+	 */
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
+
+	num_msgs = 0;
+	qb_log(LOG_ERR, "try if you: log_it_please()");
+	log_it_please();
+	ck_assert_int_eq(num_msgs, 3);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE,
+			  QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FUNCTION, __func__, LOG_DEBUG);
+
+	num_msgs = 0;
+	log_it_please();
+	ck_assert_int_eq(num_msgs, 0);
+	qb_log(LOG_DEBUG, "try if you: log_it_please()");
+	ck_assert_int_eq(num_msgs, 1);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FUNCTION,
+			  "log_also,log_and_this_too",
+			  LOG_DEBUG);
+	num_msgs = 0;
+	log_also();
+	log_and_this_too();
+	ck_assert_int_eq(num_msgs, 2);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "fakefile.c,"__FILE__",otherfakefile", LOG_DEBUG);
+	/*
+	 * make sure we can pass in a null filename or function name.
+	 */
+	qb_log_from_external_source(__func__, NULL, "%s", LOG_INFO,
+				    __LINE__, 0, "null filename");
+	qb_log_from_external_source(NULL, __FILE__, "%s", LOG_INFO,
+				    __LINE__, 0, "null function");
+
+	/* check same file/lineno logs with different formats work
+	 */
+	num_msgs = 0;
+	qb_log_from_external_source(__func__, __FILE__, "%s bla", LOG_INFO,
+				    56, 0, "filename/lineno");
+	ck_assert_int_eq(num_msgs, 1);
+	ck_assert_str_eq(test_buf, "filename/lineno bla");
+
+	num_msgs = 0;
+	qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+				    56, 0, "same filename/lineno");
+	ck_assert_int_eq(num_msgs, 1);
+	ck_assert_str_eq(test_buf, "same filename/lineno");
+
+	/* check filtering works on same file/lineno but different
+	 * log level.
+	 */
+	qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, __FILE__, LOG_INFO);
+
+	num_msgs = 0;
+	qb_log_from_external_source(__func__, __FILE__,
+				    "same filename/lineno, this level %d",
+				    LOG_INFO, 56, 0, LOG_INFO);
+	ck_assert_int_eq(num_msgs, 1);
+	ck_assert_str_eq(test_buf, "same filename/lineno, this level 6");
+
+	num_msgs = 0;
+	qb_log_from_external_source(__func__, __FILE__,
+				    "same filename/lineno, this level %d",
+				    LOG_DEBUG, 56, 0, LOG_DEBUG);
+	ck_assert_int_eq(num_msgs, 0);
+}
+END_TEST
+
+static const char *_test_tags_stringify(uint32_t tags)
+{
+	if (tags == 1) {
+		return "ONE";
+	} else if (tags == 8) {
+		return "ATE";
+	} else {
+		return "ANY";
+	}
+}
+
+START_TEST(test_log_format)
+{
+	int32_t t;
+	char cmp_str[256];
+	char host_str[256];
+
+	qb_log_init("test", LOG_USER, LOG_DEBUG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+	t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
+	qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+	qb_log_format_set(t, "%p %f %b");
+
+	qb_log(LOG_DEBUG, "Angus");
+	ck_assert_str_eq(test_buf, "debug check_log.c Angus");
+	qb_log(LOG_INFO, "Angus");
+	ck_assert_str_eq(test_buf, "info check_log.c Angus");
+	qb_log(LOG_NOTICE, "Angus");
+	ck_assert_str_eq(test_buf, "notice check_log.c Angus");
+	qb_log(LOG_WARNING, "Angus");
+	ck_assert_str_eq(test_buf, "warning check_log.c Angus");
+	qb_log(LOG_ERR, "Angus");
+	ck_assert_str_eq(test_buf, "error check_log.c Angus");
+	qb_log(LOG_CRIT, "Angus");
+	ck_assert_str_eq(test_buf, "crit check_log.c Angus");
+	qb_log(LOG_ALERT, "Angus");
+	ck_assert_str_eq(test_buf, "alert check_log.c Angus");
+	qb_log(LOG_EMERG, "Angus");
+	ck_assert_str_eq(test_buf, "emerg check_log.c Angus");
+
+	qb_log_tags_stringify_fn_set(_test_tags_stringify);
+	qb_log_format_set(t, "%g %b");
+
+	qb_logt(LOG_INFO, 0, "Angus");
+	ck_assert_str_eq(test_buf, "ANY Angus");
+	qb_logt(LOG_INFO, 1, "Angus");
+	ck_assert_str_eq(test_buf, "ONE Angus");
+	qb_logt(LOG_INFO, 5, "Angus");
+	ck_assert_str_eq(test_buf, "ANY Angus");
+	qb_logt(LOG_INFO, 8, "Angus");
+	ck_assert_str_eq(test_buf, "ATE Angus");
+
+	qb_log_format_set(t, "%-15f %b");
+	qb_log(LOG_WARNING, "Andrew");
+	ck_assert_str_eq(test_buf, "    check_log.c Andrew");
+
+	qb_log_tags_stringify_fn_set(NULL);
+
+	gethostname(host_str, 256);
+
+	qb_log_format_set(t, "%P %H %N %b");
+	qb_log(LOG_INFO, "Angus");
+	snprintf(cmp_str, 256, "%d %s test Angus", getpid(), host_str);
+	ck_assert_str_eq(test_buf, cmp_str);
+
+	qb_log_format_set(t, "%3N %H %P %b");
+	qb_log(LOG_INFO, "Angus");
+	snprintf(cmp_str, 256, "tes %s %d Angus", host_str, getpid());
+	ck_assert_str_eq(test_buf, cmp_str);
+}
+END_TEST
+
+START_TEST(test_log_enable)
+{
+	int32_t t;
+	int32_t state;
+
+	qb_log_init("test", LOG_USER, LOG_DEBUG);
+	state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
+	ck_assert_int_eq(state, QB_LOG_STATE_ENABLED);
+	state = qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0);
+	ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
+	state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
+	ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
+
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
+	ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
+
+	t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
+	qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+	qb_log_format_set(t, "%b");
+
+	qb_log(LOG_DEBUG, "Hello");
+	ck_assert_str_eq(test_buf, "Hello");
+
+	num_msgs = 0;
+	qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log(LOG_DEBUG, "Goodbye");
+	ck_assert_int_eq(num_msgs, 0);
+	qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
+	qb_log(LOG_DEBUG, "Hello again");
+	ck_assert_int_eq(num_msgs, 1);
+	ck_assert_str_eq(test_buf, "Hello again");
+}
+END_TEST
+
+#define ITERATIONS 100000
+static void *thr_send_logs_2(void *ctx)
+{
+	int32_t i;
+	printf("%s\n", __func__);
+
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log(LOG_INFO, "bla bla");
+		qb_log(LOG_INFO, "blue blue");
+		qb_log(LOG_INFO, "bra bra");
+		qb_log(LOG_INFO, "bro bro");
+		qb_log(LOG_INFO, "brown brown");
+		qb_log(LOG_INFO, "booo booo");
+		qb_log(LOG_INFO, "bogus bogus");
+		qb_log(LOG_INFO, "bungu bungu");
+	}
+	return (NULL);
+}
+
+static void *thr_send_logs_1(void *ctx)
+{
+	int32_t i;
+
+	printf("%s\n", __func__);
+	for (i = 0; i < ITERATIONS; i++) {
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "foo soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "fungus soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "fruity soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "free soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "frot soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "fresh soup");
+		qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
+					    __LINE__, 0, "fattening soup");
+
+	}
+	return (NULL);
+}
+
+#define THREADS 4
+START_TEST(test_log_threads)
+{
+	pthread_t threads[THREADS];
+	pthread_attr_t thread_attr[THREADS];
+	int32_t i;
+	int32_t rc;
+	int32_t lf;
+	void *retval;
+
+	qb_log_init("test", LOG_USER, LOG_DEBUG);
+	lf = qb_log_file_open("threads.log");
+	rc = qb_log_filter_ctl(lf, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE,
+					   __FILE__, LOG_DEBUG);
+	ck_assert_int_eq(rc, 0);
+	qb_log_format_set(lf, "[%p] [%l] %b");
+	rc = qb_log_ctl(lf, QB_LOG_CONF_ENABLED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+	rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	ck_assert_int_eq(rc, 0);
+
+	for (i = 0; i < THREADS/2; i++) {
+		pthread_attr_init(&thread_attr[i]);
+
+		pthread_attr_setdetachstate(&thread_attr[i],
+					    PTHREAD_CREATE_JOINABLE);
+		pthread_create(&threads[i], &thread_attr[i],
+			       thr_send_logs_1, NULL);
+	}
+	for (i = THREADS/2; i < THREADS; i++) {
+		pthread_attr_init(&thread_attr[i]);
+
+		pthread_attr_setdetachstate(&thread_attr[i],
+					    PTHREAD_CREATE_JOINABLE);
+		pthread_create(&threads[i], &thread_attr[i],
+			       thr_send_logs_2, NULL);
+	}
+	for (i = 0; i < THREADS; i++) {
+		pthread_join(threads[i], &retval);
+	}
+
+}
+END_TEST
+
+START_TEST(test_log_long_msg)
+{
+	int lpc;
+	int rc;
+	int i, max = 1000;
+	char *buffer = calloc(1, max);
+
+	qb_log_init("test", LOG_USER, LOG_DEBUG);
+	rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	ck_assert_int_eq(rc, 0);
+	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024);
+	ck_assert_int_eq(rc, 0);
+	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+	rc = qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	ck_assert_int_eq(rc, 0);
+
+	for (lpc = 500; lpc < max; lpc++) {
+		lpc++;
+		for(i = 0; i < max; i++) {
+			buffer[i] = 'a' + (i % 10);
+		}
+		buffer[lpc%600] = 0;
+		qb_log(LOG_INFO, "Message %d %d - %s", lpc, lpc%600, buffer);
+	}
+
+        qb_log_blackbox_write_to_file("blackbox.dump");
+        qb_log_blackbox_print_from_file("blackbox.dump");
+	unlink("blackbox.dump");
+	qb_log_fini();
+}
+END_TEST
+
+START_TEST(test_threaded_logging)
+{
+	int32_t t;
+	int32_t rc;
+
+	qb_log_init("test", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+	t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
+	rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
+			       QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	ck_assert_int_eq(rc, 0);
+	qb_log_format_set(t, "%b");
+	rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+	rc = qb_log_ctl(t, QB_LOG_CONF_THREADED, QB_TRUE);
+	ck_assert_int_eq(rc, 0);
+	qb_log_thread_start();
+
+	memset(test_buf, 0, sizeof(test_buf));
+	test_priority = 0;
+	num_msgs = 0;
+
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+	qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
+
+	qb_log_fini();
+
+	ck_assert_int_eq(num_msgs, 10);
+}
+END_TEST
+
+static Suite *
+log_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("logging");
+
+	tc = tcase_create("va_serialize");
+	tcase_add_test(tc, test_va_serialize);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("limits");
+	tcase_add_test(tc, test_log_stupid_inputs);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("basic");
+	tcase_add_test(tc, test_log_basic);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("format");
+	tcase_add_test(tc, test_log_format);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("enable");
+	tcase_add_test(tc, test_log_enable);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("threads");
+	tcase_add_test(tc, test_log_threads);
+	tcase_set_timeout(tc, 360);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("long_msg");
+	tcase_add_test(tc, test_log_long_msg);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("filter_ft");
+	tcase_add_test(tc, test_log_filter_fn);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("threaded_logging");
+	tcase_add_test(tc, test_threaded_logging);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t
+main(void)
+{
+	int32_t number_failed;
+
+	Suite *s = log_suite();
+	SRunner *sr = srunner_create(s);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_loop.c b/tests/check_loop.c
new file mode 100644
index 0000000..72decf3
--- /dev/null
+++ b/tests/check_loop.c
@@ -0,0 +1,774 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qbloop.h>
+#include <qb/qblog.h>
+
+static int32_t job_1_run_count = 0;
+static int32_t job_2_run_count = 0;
+static int32_t job_3_run_count = 0;
+
+
+static int32_t job_order_1 = 1;
+static int32_t job_order_2 = 2;
+static int32_t job_order_3 = 3;
+static int32_t job_order_4 = 4;
+static int32_t job_order_5 = 5;
+static int32_t job_order_6 = 6;
+static int32_t job_order_7 = 7;
+static int32_t job_order_8 = 8;
+static int32_t job_order_9 = 9;
+static int32_t job_order_10 = 10;
+static int32_t job_order_11 = 11;
+static int32_t job_order_12 = 12;
+static int32_t job_order_13 = 13;
+
+
+static void job_1(void *data)
+{
+	job_1_run_count++;
+}
+
+static void job_order_check(void *data)
+{
+	int32_t * order = (int32_t *)data;
+	job_1_run_count++;
+	ck_assert_int_eq(job_1_run_count, *order);
+
+	if (job_1_run_count == 1) {
+		qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_10, job_order_check);
+		qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_11, job_order_check);
+		qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_12, job_order_check);
+		qb_loop_job_add(NULL, QB_LOOP_MED, &job_order_13, job_order_check);
+	} else if (job_1_run_count >= 13) {
+		qb_loop_stop(NULL);
+	}
+}
+
+static void job_stop(void *data)
+{
+	qb_loop_t *l = (qb_loop_t *)data;
+	job_3_run_count++;
+	qb_loop_stop(l);
+}
+
+static void job_2(void *data)
+{
+	int32_t res;
+	qb_loop_t *l = (qb_loop_t *)data;
+	job_2_run_count++;
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_stop);
+	ck_assert_int_eq(res, 0);
+}
+
+static void job_1_r(void *data)
+{
+	int32_t res;
+	qb_loop_t *l = (qb_loop_t *)data;
+	job_1_run_count++;
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_2);
+	ck_assert_int_eq(res, 0);
+}
+
+static void job_1_add_nuts(void *data)
+{
+	int32_t res;
+	qb_loop_t *l = (qb_loop_t *)data;
+	job_1_run_count++;
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1);
+	ck_assert_int_eq(res, 0);
+	if (job_1_run_count < 500) {
+		res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_1_add_nuts);
+		ck_assert_int_eq(res, 0);
+	} else {
+		res = qb_loop_job_add(l, QB_LOOP_LOW, data, job_stop);
+		ck_assert_int_eq(res, 0);
+	}
+	ck_assert_int_eq(res, 0);
+}
+
+START_TEST(test_loop_job_input)
+{
+	int32_t res;
+	qb_loop_t *l;
+
+	res = qb_loop_job_add(NULL, QB_LOOP_LOW,  NULL, job_2);
+	ck_assert_int_eq(res, -EINVAL);
+
+	l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_job_add(NULL, QB_LOOP_LOW,  NULL, job_2);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, 89,  NULL, job_2);
+	ck_assert_int_eq(res, -EINVAL);
+	res = qb_loop_job_add(l, QB_LOOP_LOW,  NULL, NULL);
+	ck_assert_int_eq(res, -EINVAL);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+START_TEST(test_loop_job_1)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_LOW,  l, job_stop);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	ck_assert_int_eq(job_1_run_count, 1);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+START_TEST(test_loop_job_4)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_job_add(l, QB_LOOP_LOW,  l, job_1_r);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	ck_assert_int_eq(job_1_run_count, 1);
+	ck_assert_int_eq(job_2_run_count, 1);
+	ck_assert_int_eq(job_3_run_count, 1);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+START_TEST(test_loop_job_nuts)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_job_add(l, QB_LOOP_LOW,  l, job_1_add_nuts);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	fail_if(job_1_run_count < 500);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+START_TEST(test_loop_job_order)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	job_1_run_count = 0;
+
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_1, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_2, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_3, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_4, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_5, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_6, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_7, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_8, job_order_check);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, &job_order_9, job_order_check);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+
+static qb_util_stopwatch_t *rl_sw;
+#define RATE_LIMIT_RUNTIME_SEC 3
+
+static void job_add_self(void *data)
+{
+	int32_t res;
+	uint64_t elapsed1;
+	qb_loop_t *l = (qb_loop_t *)data;
+
+	job_1_run_count++;
+	qb_util_stopwatch_stop(rl_sw);
+	elapsed1 = qb_util_stopwatch_us_elapsed_get(rl_sw);
+	if (elapsed1 > (RATE_LIMIT_RUNTIME_SEC * QB_TIME_US_IN_SEC)) {
+		/* run for 3 seconds */
+		qb_loop_stop(l);
+		return;
+	}
+	res = qb_loop_job_add(l, QB_LOOP_MED, data, job_add_self);
+	ck_assert_int_eq(res, 0);
+}
+
+START_TEST(test_job_rate_limit)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	rl_sw = qb_util_stopwatch_create();
+	fail_if(rl_sw == NULL);
+
+	qb_util_stopwatch_start(rl_sw);
+
+	res = qb_loop_job_add(l, QB_LOOP_MED,  l, job_add_self);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	/*
+	 * the test is to confirm that a single job does not run away
+	 * and cause cpu spin. We are going to say that a spin is more than
+	 * one job per 50ms if there is only one job pending in the loop.
+	 */
+	_ck_assert_int(job_1_run_count, <, (RATE_LIMIT_RUNTIME_SEC * (QB_TIME_MS_IN_SEC/50)) + 10);
+	qb_loop_destroy(l);
+	qb_util_stopwatch_free(rl_sw);
+}
+END_TEST
+
+static void job_stop_and_del_1(void *data)
+{
+	int32_t res;
+	qb_loop_t *l = (qb_loop_t *)data;
+	job_3_run_count++;
+	res = qb_loop_job_del(l, QB_LOOP_MED, l, job_1);
+	ck_assert_int_eq(res, 0);
+	qb_loop_stop(l);
+}
+
+START_TEST(test_job_add_del)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_job_add(l, QB_LOOP_MED, l, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_del(l, QB_LOOP_MED, l, job_1);
+	ck_assert_int_eq(res, 0);
+
+	job_1_run_count = 0;
+	job_3_run_count = 0;
+
+	res = qb_loop_job_add(l, QB_LOOP_MED, l, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, l, job_stop_and_del_1);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+	ck_assert_int_eq(job_1_run_count, 0);
+	ck_assert_int_eq(job_3_run_count, 1);
+
+	qb_loop_destroy(l);
+}
+END_TEST
+
+
+static Suite *loop_job_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("loop_job");
+
+	tc = tcase_create("limits");
+	tcase_add_test(tc, test_loop_job_input);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("run_one");
+	tcase_add_test(tc, test_loop_job_1);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("run_recursive");
+	tcase_add_test(tc, test_loop_job_4);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("run_500");
+	tcase_add_test(tc, test_loop_job_nuts);
+	tcase_set_timeout(tc, 5);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("rate_limit");
+	tcase_add_test(tc, test_job_rate_limit);
+	tcase_set_timeout(tc, 5);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("add_del");
+	tcase_add_test(tc, test_job_add_del);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("order");
+	tcase_add_test(tc, test_loop_job_order);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *  Timers
+ */
+static qb_loop_timer_handle test_th;
+
+START_TEST(test_loop_timer_input)
+{
+	int32_t res;
+	qb_loop_t *l;
+
+	res = qb_loop_timer_add(NULL, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, NULL, job_2, &test_th);
+	ck_assert_int_eq(res, -EINVAL);
+
+	l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_timer_add(NULL, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, NULL, job_2, &test_th);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, NULL, &test_th);
+	ck_assert_int_eq(res, -EINVAL);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+static void one_shot_tmo(void * data)
+{
+	static int32_t been_here = QB_FALSE;
+	ck_assert_int_eq(been_here, QB_FALSE);
+	been_here = QB_TRUE;
+}
+
+static qb_loop_timer_handle reset_th;
+static int32_t reset_timer_step = 0;
+static void reset_one_shot_tmo(void*data)
+{
+	int32_t res;
+	qb_loop_t *l = data;
+	if (reset_timer_step == 0) {
+		res = qb_loop_timer_del(l, reset_th);
+		ck_assert_int_eq(res, -EINVAL);
+		res = qb_loop_timer_is_running(l, reset_th);
+		ck_assert_int_eq(res, QB_FALSE);
+		res = qb_loop_timer_add(l, QB_LOOP_LOW, 8*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th);
+		ck_assert_int_eq(res, 0);
+	}
+
+	reset_timer_step++;
+}
+
+START_TEST(test_loop_timer_basic)
+{
+	int32_t res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, one_shot_tmo, &test_th);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_loop_timer_is_running(l, test_th);
+	ck_assert_int_eq(res, QB_TRUE);
+
+	res = qb_loop_timer_add(l, QB_LOOP_LOW, 7*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_loop_timer_add(l, QB_LOOP_LOW, 60*QB_TIME_NS_IN_MSEC, l, job_stop, &test_th);
+	ck_assert_int_eq(res, 0);
+
+	qb_loop_run(l);
+
+	ck_assert_int_eq(reset_timer_step, 2);
+
+	qb_loop_destroy(l);
+}
+END_TEST
+
+struct qb_stop_watch {
+	uint64_t start;
+	uint64_t end;
+	qb_loop_t *l;
+	uint64_t ns_timer;
+	int64_t total;
+	int32_t count;
+	int32_t killer;
+	qb_loop_timer_handle th;
+};
+
+static void stop_watch_tmo(void*data)
+{
+	struct qb_stop_watch *sw = (struct qb_stop_watch *)data;
+	float per;
+	int64_t diff;
+
+	sw->end = qb_util_nano_current_get();
+
+	diff = sw->end - sw->start;
+	if (diff < sw->ns_timer) {
+		printf("timer expired early! by %"PRIi64"\n", (int64_t)(sw->ns_timer - diff));
+	}
+	ck_assert(diff >= sw->ns_timer);
+	sw->total += diff;
+	sw->total -= sw->ns_timer;
+	sw->start = sw->end;
+
+	sw->count++;
+	if (sw->count < 50) {
+		qb_loop_timer_add(sw->l, QB_LOOP_LOW, sw->ns_timer, data, stop_watch_tmo, &sw->th);
+	} else {
+		per = ((sw->total * 100) / sw->count) / (float)sw->ns_timer;
+		printf("average error for %"PRIu64" ns timer is %"PRIi64" (ns) (%f)\n",
+		       sw->ns_timer,
+		       sw->total/sw->count, per);
+		if (sw->killer) {
+			qb_loop_stop(sw->l);
+		}
+	}
+}
+
+static void start_timer(qb_loop_t *l, struct qb_stop_watch *sw, uint64_t timeout, int32_t killer)
+{
+	int32_t res;
+
+	sw->l = l;
+	sw->count = 0;
+	sw->total = 0;
+	sw->killer = killer;
+	sw->ns_timer = timeout;
+	sw->start = qb_util_nano_current_get();
+	res = qb_loop_timer_add(sw->l, QB_LOOP_LOW, sw->ns_timer, sw, stop_watch_tmo, &sw->th);
+	ck_assert_int_eq(res, 0);
+}
+
+
+START_TEST(test_loop_timer_precision)
+{
+	int32_t i;
+	uint64_t tmo;
+	struct qb_stop_watch sw[11];
+	qb_loop_t *l = qb_loop_create();
+
+	fail_if(l == NULL);
+
+	for (i = 0; i < 10; i++) {
+		tmo = ((1 + i * 9) * QB_TIME_NS_IN_MSEC) + 500000;
+		start_timer(l, &sw[i], tmo, QB_FALSE);
+	}
+	start_timer(l, &sw[i], 100 * QB_TIME_NS_IN_MSEC, QB_TRUE);
+
+	qb_loop_run(l);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+static int expire_leak_counter = 0;
+#define EXPIRE_NUM_RUNS 10
+static int expire_leak_runs = 0;
+
+static void empty_func_tmo(void*data)
+{
+	expire_leak_counter++;
+}
+
+static void stop_func_tmo(void*data)
+{
+	qb_loop_t *l = (qb_loop_t *)data;
+	qb_log(LOG_DEBUG, "expire_leak_counter:%d",  expire_leak_counter);
+	qb_loop_stop(l);
+}
+
+static void next_func_tmo(void*data)
+{
+	qb_loop_t *l = (qb_loop_t *)data;
+	int32_t i;
+	uint64_t tmo;
+	uint64_t max_tmo = 0;
+	qb_loop_timer_handle th;
+
+	qb_log(LOG_DEBUG, "expire_leak_counter:%d",  expire_leak_counter);
+	for (i = 0; i < 300; i++) {
+		tmo = ((1 + i) * QB_TIME_NS_IN_MSEC) + 500000;
+		qb_loop_timer_add(l, QB_LOOP_LOW, tmo, NULL, empty_func_tmo, &th);
+		qb_loop_timer_add(l, QB_LOOP_MED, tmo, NULL, empty_func_tmo, &th);
+		qb_loop_timer_add(l, QB_LOOP_HIGH, tmo, NULL, empty_func_tmo, &th);
+		max_tmo = QB_MAX(max_tmo, tmo);
+	}
+	expire_leak_runs++;
+	if (expire_leak_runs == EXPIRE_NUM_RUNS) {
+		qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, stop_func_tmo, &th);
+	} else {
+		qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, next_func_tmo, &th);
+	}
+}
+
+/*
+ * make sure that file descriptors don't get leaked with no qb_loop_timer_del()
+ */
+START_TEST(test_loop_timer_expire_leak)
+{
+	int32_t i;
+	uint64_t tmo;
+	uint64_t max_tmo = 0;
+	qb_loop_timer_handle th;
+	qb_loop_t *l = qb_loop_create();
+
+	fail_if(l == NULL);
+
+	expire_leak_counter = 0;
+	for (i = 0; i < 300; i++) {
+		tmo = ((1 + i) * QB_TIME_NS_IN_MSEC) + 500000;
+		qb_loop_timer_add(l, QB_LOOP_LOW, tmo, NULL, empty_func_tmo, &th);
+		qb_loop_timer_add(l, QB_LOOP_MED, tmo, NULL, empty_func_tmo, &th);
+		qb_loop_timer_add(l, QB_LOOP_HIGH, tmo, NULL, empty_func_tmo, &th);
+		max_tmo = QB_MAX(max_tmo, tmo);
+	}
+	qb_loop_timer_add(l, QB_LOOP_LOW, max_tmo, l, next_func_tmo, &th);
+	expire_leak_runs = 1;
+
+	qb_loop_run(l);
+
+	ck_assert_int_eq(expire_leak_counter, 300*3* EXPIRE_NUM_RUNS);
+	qb_loop_destroy(l);
+}
+END_TEST
+
+static int received_signum = 0;
+static int received_sigs = 0;
+
+static int32_t
+sig_handler(int32_t rsignal, void *data)
+{
+	qb_loop_t *l = (qb_loop_t *)data;
+	qb_log(LOG_DEBUG, "caught signal %d", rsignal);
+	received_signum = rsignal;
+	received_sigs++;
+	qb_loop_job_add(l, QB_LOOP_LOW, NULL, job_stop);
+	return 0;
+}
+
+START_TEST(test_loop_sig_handling)
+{
+	qb_loop_signal_handle handle;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	qb_loop_signal_add(l, QB_LOOP_HIGH, SIGINT,
+			   l, sig_handler, &handle);
+	qb_loop_signal_add(l, QB_LOOP_HIGH, SIGTERM,
+			   l, sig_handler, &handle);
+	qb_loop_signal_add(l, QB_LOOP_HIGH, SIGQUIT,
+			   l, sig_handler, &handle);
+	kill(getpid(), SIGINT);
+	qb_loop_run(l);
+	ck_assert_int_eq(received_signum, SIGINT);
+	kill(getpid(), SIGQUIT);
+	qb_loop_run(l);
+	ck_assert_int_eq(received_signum, SIGQUIT);
+
+	qb_loop_destroy(l);
+}
+END_TEST
+
+START_TEST(test_loop_sig_only_get_one)
+{
+	int res;
+	qb_loop_signal_handle handle;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	/* make sure we only get one call to the handler
+	 * don't assume we are going to exit the loop.
+	 */
+	received_sigs = 0;
+	qb_loop_signal_add(l, QB_LOOP_LOW, SIGINT,
+			   l, sig_handler, &handle);
+
+	res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_MED, NULL, job_1);
+	ck_assert_int_eq(res, 0);
+
+	kill(getpid(), SIGINT);
+	qb_loop_run(l);
+
+	ck_assert_int_eq(received_signum, SIGINT);
+	ck_assert_int_eq(received_sigs, 1);
+
+	qb_loop_destroy(l);
+}
+END_TEST
+
+static qb_loop_signal_handle sig_hdl;
+
+static void
+job_rm_sig_handler(void *data)
+{
+	int res;
+	qb_loop_t *l = (qb_loop_t *)data;
+	res = qb_loop_signal_del(l, sig_hdl);
+	ck_assert_int_eq(res, 0);
+	res = qb_loop_job_add(l, QB_LOOP_LOW, NULL, job_stop);
+	ck_assert_int_eq(res, 0);
+}
+
+START_TEST(test_loop_sig_delete)
+{
+	int res;
+	qb_loop_t *l = qb_loop_create();
+	fail_if(l == NULL);
+
+	/* make sure we can remove a signal job from the job queue.
+	 */
+	received_sigs = 0;
+	received_signum = 0;
+	res = qb_loop_signal_add(l, QB_LOOP_MED, SIGINT,
+				 l, sig_handler, &sig_hdl);
+	ck_assert_int_eq(res, 0);
+
+	res = qb_loop_job_add(l, QB_LOOP_HIGH, NULL,
+			      job_rm_sig_handler);
+	ck_assert_int_eq(res, 0);
+
+	kill(getpid(), SIGINT);
+	qb_loop_run(l);
+
+	ck_assert_int_eq(received_sigs, 0);
+	ck_assert_int_eq(received_signum, 0);
+
+	qb_loop_destroy(l);
+}
+END_TEST
+
+static Suite *
+loop_timer_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("loop_timers");
+
+	tc = tcase_create("limits");
+	tcase_add_test(tc, test_loop_timer_input);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("basic");
+	tcase_add_test(tc, test_loop_timer_basic);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("precision");
+	tcase_add_test(tc, test_loop_timer_precision);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("expire_leak");
+	tcase_add_test(tc, test_loop_timer_expire_leak);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+	return s;
+}
+
+static Suite *
+loop_signal_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("loop_signal_suite");
+
+	tc = tcase_create("signals");
+	tcase_add_test(tc, test_loop_sig_handling);
+	tcase_set_timeout(tc, 10);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("sig_only_one");
+	tcase_add_test(tc, test_loop_sig_only_get_one);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("sig_delete");
+	tcase_add_test(tc, test_loop_sig_delete);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t
+main(void)
+{
+	int32_t number_failed;
+	SRunner *sr = srunner_create(loop_job_suite());
+	srunner_add_suite (sr, loop_timer_suite());
+	srunner_add_suite (sr, loop_signal_suite());
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_map.c b/tests/check_map.c
new file mode 100644
index 0000000..f0053f0
--- /dev/null
+++ b/tests/check_map.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <check.h>
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <qb/qbmap.h>
+
+const char *chars[] = {
+	"0","1","2","3","4","5","6","7","8","9",
+	"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
+	"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
+	NULL,
+};
+
+const char *chars2[] = {
+	"0","1","2","3","4","5","6","7","8","9",
+	"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
+	NULL,
+};
+
+static char *notified_key = NULL;
+static void *notified_value = NULL;
+static void *notified_new_value = NULL;
+static void *notified_user_data = NULL;
+static int32_t notified_event = 0;
+static int32_t notified_event_prev = 0;
+static int32_t notified_events = 0;
+
+static void
+my_map_notification_iter(uint32_t event,
+			 char* key, void* old_value,
+			 void* value, void* user_data)
+{
+	const char *p;
+	void *data;
+	qb_map_t *m = (qb_map_t *)user_data;
+	qb_map_iter_t *it = qb_map_iter_create(m);
+
+	notified_events++;
+
+	for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) {
+		printf("%s > %s\n", p, (char*) data);
+	}
+	qb_map_iter_free(it);
+}
+
+/*
+ * create some entries
+ * add a notifier
+ * delete an entry
+ * in the notifier iterate over the map.
+ */
+static void
+test_map_notifications_iter(qb_map_t *m)
+{
+	int i;
+
+	qb_map_put(m, "k1", "one");
+	qb_map_put(m, "k12", "two");
+	qb_map_put(m, "k34", "three");
+	ck_assert_int_eq(qb_map_count_get(m), 3);
+
+	notified_events = 0;
+	i = qb_map_notify_add(m, NULL, my_map_notification_iter,
+			      (QB_MAP_NOTIFY_DELETED |
+			       QB_MAP_NOTIFY_RECURSIVE), m);
+	ck_assert_int_eq(i, 0);
+	qb_map_rm(m, "k12");
+	ck_assert_int_eq(notified_events, 1);
+	ck_assert_int_eq(qb_map_count_get(m), 2);
+}
+
+static void
+test_map_simple(qb_map_t *m, const char *name)
+{
+	int i;
+	const char *p;
+	void *data;
+	qb_map_iter_t *it;
+
+	qb_map_put(m, "k1", "one");
+	qb_map_put(m, "k12", "two");
+	qb_map_put(m, "k34", "three");
+	ck_assert_int_eq(qb_map_count_get(m), 3);
+	qb_map_put(m, "k3", "four");
+	ck_assert_int_eq(qb_map_count_get(m), 4);
+
+	it = qb_map_iter_create(m);
+	i = 0;
+	for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) {
+		printf("%25s(%d) %s > %s\n", name, i, p, (char*) data);
+		i++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_int_eq(i, 4);
+
+	ck_assert_str_eq(qb_map_get(m, "k34"), "three");
+	ck_assert_str_eq(qb_map_get(m, "k1"), "one");
+	ck_assert_str_eq(qb_map_get(m, "k12"), "two");
+	ck_assert_str_eq(qb_map_get(m, "k3"), "four");
+
+	qb_map_rm(m, "k12");
+	ck_assert_int_eq(qb_map_count_get(m), 3);
+	qb_map_put(m, "9k", "nine");
+
+	qb_map_put(m, "k34", "not_three");
+	ck_assert_str_eq(qb_map_get(m, "k34"), "not_three");
+	ck_assert_int_eq(qb_map_count_get(m), 4);
+
+	qb_map_destroy(m);
+}
+
+static int32_t
+my_traverse(const char *key, void *value, void *data)
+{
+	ck_assert((*key) > 0);
+	return QB_FALSE;
+}
+
+static int32_t
+check_order(const char *key, void *value, void *data)
+{
+	int *o = (int*)data;
+	ck_assert_str_eq(chars[*o], key);
+	ck_assert_str_eq(chars[*o], value);
+	(*o)++;
+	return QB_FALSE;
+}
+
+static int32_t
+check_order2(const char *key, void *value, void *data)
+{
+	int *o = (int*)data;
+	ck_assert_str_eq(chars2[*o], key);
+	ck_assert_str_eq(chars2[*o], value);
+	(*o)++;
+	return QB_FALSE;
+}
+
+static void
+test_map_search(qb_map_t* m)
+{
+	int32_t i;
+	int32_t removed;
+	int order;
+	char c[2];
+	const char *p;
+
+	for (i = 0; chars[i]; i++) {
+		qb_map_put(m, chars[i], chars[i]);
+	}
+	qb_map_foreach(m, my_traverse, NULL);
+
+	ck_assert_int_eq(qb_map_count_get(m), (26*2 + 10));
+
+	order = 0;
+	qb_map_foreach(m, check_order, &order);
+
+	for (i = 0; i < 26; i++) {
+		removed = qb_map_rm(m, chars[i + 10]);
+		ck_assert(removed);
+	}
+
+	c[0] = '\0';
+	c[1] = '\0';
+	removed = qb_map_rm(m, c);
+	ck_assert(!removed);
+
+	qb_map_foreach(m, my_traverse, NULL);
+
+	ck_assert_int_eq(qb_map_count_get(m), 26+10);
+
+	order = 0;
+	qb_map_foreach(m, check_order2, &order);
+
+	for (i = 25; i >= 0; i--) {
+		qb_map_put(m, chars[i + 10], chars[i + 10]);
+	}
+	order = 0;
+	qb_map_foreach(m, check_order, &order);
+
+	c[0] = '0';
+	p = qb_map_get(m, c);
+	ck_assert(p && *p == *c);
+
+	c[0] = 'A';
+	p = qb_map_get(m, c);
+	ck_assert(p && *p == *c);
+
+	c[0] = 'a';
+	p = qb_map_get(m, c);
+	ck_assert(p && *p == *c);
+
+	c[0] = 'z';
+	p = qb_map_get(m, c);
+	ck_assert(p && *p == *c);
+
+	c[0] = '!';
+	p = qb_map_get(m, c);
+	ck_assert(p == NULL);
+
+	c[0] = '=';
+	p = qb_map_get(m, c);
+	ck_assert(p == NULL);
+
+	c[0] = '|';
+	p = qb_map_get(m, c);
+	ck_assert(p == NULL);
+
+	qb_map_destroy(m);
+}
+
+static void
+my_map_notification(uint32_t event,
+		    char* key, void* old_value,
+		    void* value, void* user_data)
+{
+	notified_key = key;
+	notified_value = old_value;
+	notified_new_value = value;
+	notified_user_data = user_data;
+	notified_event_prev = notified_event;
+	notified_event = event;
+}
+
+static void
+my_map_notification_2(uint32_t event,
+		      char* key, void* old_value,
+		      void* value, void* user_data)
+{
+}
+
+static void
+test_map_remove(qb_map_t *m)
+{
+	const char * a, *b, *c, *d;
+	int32_t i;
+	int32_t removed;
+	const char *remove_ch[] = {"o","m","k","j","i","g","f","e","d","b","a",	NULL};
+
+	i = qb_map_notify_add(m, NULL, my_map_notification,
+			      (QB_MAP_NOTIFY_DELETED|
+			       QB_MAP_NOTIFY_REPLACED|
+			       QB_MAP_NOTIFY_RECURSIVE),
+			      m);
+	ck_assert_int_eq(i, 0);
+
+	for (i = 0; chars[i]; i++) {
+		qb_map_put(m, chars[i], chars[i]);
+	}
+
+	a = "0";
+	qb_map_put(m, a, a);
+	ck_assert(notified_key == chars[0]);
+	ck_assert(notified_value == chars[0]);
+	ck_assert(notified_user_data == m);
+	notified_key = NULL;
+	notified_value = NULL;
+	notified_user_data = NULL;
+
+	b = "5";
+	removed = qb_map_rm(m, b);
+	ck_assert(removed);
+	ck_assert(notified_key == chars[5]);
+	ck_assert(notified_value == chars[5]);
+	ck_assert(notified_user_data == m);
+	notified_key = NULL;
+	notified_value = NULL;
+	notified_user_data = NULL;
+
+	d = "1";
+	qb_map_put(m, d, d);
+	ck_assert(notified_key == chars[1]);
+	ck_assert(notified_value == chars[1]);
+	ck_assert(notified_user_data == m);
+	notified_key = NULL;
+	notified_value = NULL;
+
+	c = "2";
+	removed = qb_map_rm(m, c);
+	ck_assert(removed);
+	ck_assert(notified_key == chars[2]);
+	ck_assert(notified_value == chars[2]);
+	notified_key = NULL;
+	notified_value = NULL;
+
+	for (i = 0; remove_ch[i]; i++) {
+		removed = qb_map_rm(m, remove_ch[i]);
+		ck_assert(removed);
+	}
+
+	qb_map_destroy(m);
+}
+
+static void
+test_map_notifications_basic(qb_map_t *m)
+{
+	int32_t i;
+
+/* with global notifier */
+	i = qb_map_notify_add(m, NULL, my_map_notification,
+			      (QB_MAP_NOTIFY_INSERTED|
+			       QB_MAP_NOTIFY_DELETED|
+			       QB_MAP_NOTIFY_REPLACED|
+			       QB_MAP_NOTIFY_RECURSIVE),
+			       m);
+	ck_assert_int_eq(i, 0);
+
+	notified_key = NULL;
+	notified_value = NULL;
+	notified_new_value = NULL;
+
+/* insert */
+	qb_map_put(m, "garden", "grow");
+	ck_assert_str_eq(notified_key, "garden");
+	ck_assert_str_eq(notified_new_value, "grow");
+	ck_assert(notified_user_data == m);
+
+/* update */
+	qb_map_put(m, "garden", "green");
+	ck_assert_str_eq(notified_key, "garden");
+	ck_assert_str_eq(notified_value, "grow");
+	ck_assert_str_eq(notified_new_value, "green");
+	ck_assert(notified_user_data == m);
+
+/* delete */
+	qb_map_rm(m, "garden");
+	ck_assert_str_eq(notified_key, "garden");
+	ck_assert_str_eq(notified_value, "green");
+	ck_assert(notified_user_data == m);
+
+/* no event with notifier removed */
+	i = qb_map_notify_del(m, NULL, my_map_notification,
+			      (QB_MAP_NOTIFY_INSERTED|
+			       QB_MAP_NOTIFY_DELETED|
+			       QB_MAP_NOTIFY_REPLACED|
+			       QB_MAP_NOTIFY_RECURSIVE));
+	ck_assert_int_eq(i, 0);
+	notified_key = NULL;
+	notified_value = NULL;
+	notified_new_value = NULL;
+	qb_map_put(m, "age", "67");
+	ck_assert(notified_key == NULL);
+	ck_assert(notified_value == NULL);
+	ck_assert(notified_new_value == NULL);
+
+/* deleting a non-existing notification */
+	i = qb_map_notify_del(m, "a", my_map_notification,
+			      (QB_MAP_NOTIFY_INSERTED|
+			       QB_MAP_NOTIFY_DELETED|
+			       QB_MAP_NOTIFY_REPLACED|
+			       QB_MAP_NOTIFY_RECURSIVE));
+	ck_assert_int_eq(i, -ENOENT);
+
+/* test uniquess */
+	qb_map_put(m, "fred", "null");
+	i = qb_map_notify_add(m, "fred", my_map_notification,
+			      QB_MAP_NOTIFY_REPLACED, m);
+	ck_assert_int_eq(i, 0);
+	i = qb_map_notify_add(m, "fred", my_map_notification,
+			      QB_MAP_NOTIFY_REPLACED, m);
+	ck_assert_int_eq(i, -EEXIST);
+}
+
+/* test free'ing notifier
+ *
+ * input:
+ *   only one can be added
+ *   can only be added with NULL key (global)
+ * output:
+ *   is the last notifier called (after deleted or replaced)
+ *   recursive is implicit
+ */
+static void
+test_map_notifications_free(qb_map_t *m)
+{
+	int32_t i;
+	i = qb_map_notify_add(m, "not global", my_map_notification,
+			      QB_MAP_NOTIFY_FREE, m);
+	ck_assert_int_eq(i, -EINVAL);
+	i = qb_map_notify_add(m, NULL, my_map_notification,
+			      QB_MAP_NOTIFY_FREE, m);
+	ck_assert_int_eq(i, 0);
+	i = qb_map_notify_add(m, NULL, my_map_notification_2,
+			      QB_MAP_NOTIFY_FREE, m);
+	ck_assert_int_eq(i, -EEXIST);
+	i = qb_map_notify_del_2(m, NULL, my_map_notification,
+			      QB_MAP_NOTIFY_FREE, m);
+	ck_assert_int_eq(i, 0);
+	i = qb_map_notify_add(m, NULL, my_map_notification,
+			      (QB_MAP_NOTIFY_FREE |
+			       QB_MAP_NOTIFY_REPLACED |
+			       QB_MAP_NOTIFY_DELETED |
+			       QB_MAP_NOTIFY_RECURSIVE), m);
+	ck_assert_int_eq(i, 0);
+
+	qb_map_put(m, "garden", "grow");
+
+/* update */
+	qb_map_put(m, "garden", "green");
+	ck_assert_int_eq(notified_event_prev, QB_MAP_NOTIFY_REPLACED);
+	ck_assert_int_eq(notified_event, QB_MAP_NOTIFY_FREE);
+
+/* delete */
+	qb_map_rm(m, "garden");
+	ck_assert_int_eq(notified_event_prev, QB_MAP_NOTIFY_DELETED);
+	ck_assert_int_eq(notified_event, QB_MAP_NOTIFY_FREE);
+}
+
+static void
+test_map_notifications_prefix(qb_map_t *m)
+{
+	int32_t i;
+
+
+/* with prefix notifier */
+	i = qb_map_notify_add(m, "add", my_map_notification,
+			      (QB_MAP_NOTIFY_INSERTED|
+			       QB_MAP_NOTIFY_DELETED|
+			       QB_MAP_NOTIFY_REPLACED|
+			       QB_MAP_NOTIFY_RECURSIVE),
+			       &i);
+	ck_assert_int_eq(i, 0);
+
+/* insert */
+	qb_map_put(m, "adder", "snake");
+	ck_assert_str_eq(notified_key, "adder");
+	ck_assert_str_eq(notified_new_value, "snake");
+	ck_assert(notified_user_data == &i);
+
+/* insert (no match) */
+	notified_key = NULL;
+	notified_value = NULL;
+	notified_new_value = NULL;
+	qb_map_put(m, "adjust", "it");
+	ck_assert(notified_key == NULL);
+	ck_assert(notified_value == NULL);
+	ck_assert(notified_new_value == NULL);
+
+/* update */
+	qb_map_put(m, "adder", "+++");
+	ck_assert_str_eq(notified_key, "adder");
+	ck_assert_str_eq(notified_value, "snake");
+	ck_assert_str_eq(notified_new_value, "+++");
+
+/* delete */
+	qb_map_rm(m, "adder");
+	ck_assert_str_eq(notified_key, "adder");
+	ck_assert_str_eq(notified_value, "+++");
+
+}
+
+static void
+test_map_traverse_ordered(qb_map_t *m)
+{
+	int32_t i;
+	const char *p;
+	char *result;
+	void *data;
+	qb_map_iter_t *it = qb_map_iter_create(m);
+
+	for (i = 0; chars[i]; i++) {
+		qb_map_put(m, chars[i], chars[i]);
+	}
+	result = calloc(sizeof(char), 26 * 2 + 10 + 1);
+
+	i = 0;
+	for (p = qb_map_iter_next(it, &data); p; p = qb_map_iter_next(it, &data)) {
+		result[i] = *(char*) data;
+		i++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_str_eq(result,
+			 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+
+	qb_map_destroy(m);
+}
+
+static int32_t
+traverse_and_remove_func(const char *key, void *value, void *data)
+{
+	int kk = random() % 30;
+	qb_map_t *m = (qb_map_t *)data;
+	qb_map_rm(m, chars[kk]);
+	qb_map_put(m, chars[kk+30], key);
+	return QB_FALSE;
+}
+
+static void
+test_map_iter_safety(qb_map_t *m, int32_t ordered)
+{
+	void *data;
+	void *data2;
+	const char *p;
+	const char *p2;
+	qb_map_iter_t *it;
+	qb_map_iter_t *it2;
+	int32_t found_good = QB_FALSE;
+
+	qb_map_put(m, "aaaa", "aye");
+	qb_map_put(m, "bbbb", "bee");
+	qb_map_put(m, "cccc", "sea");
+
+	it = qb_map_iter_create(m);
+	it2 = qb_map_iter_create(m);
+	while ((p = qb_map_iter_next(it, &data)) != NULL) {
+		printf("1: %s == %s\n", p, (char*)data);
+		if (strcmp(p, "bbbb") == 0) {
+			qb_map_rm(m, "bbbb");
+			qb_map_rm(m, "cccc");
+			qb_map_put(m, "fffff", "yum");
+			while ((p2 = qb_map_iter_next(it2, &data2)) != NULL) {
+				printf("2: %s == %s\n", p2, (char*)data2);
+				if (strcmp(p2, "fffff") == 0) {
+					qb_map_put(m, "ggggg", "good");
+				}
+			}
+			qb_map_iter_free(it2);
+		}
+		if (strcmp(p, "ggggg") == 0) {
+			found_good = QB_TRUE;
+		}
+	}
+	qb_map_iter_free(it);
+
+	if (ordered) {
+		ck_assert_int_eq(found_good, QB_TRUE);
+	}
+
+	qb_map_destroy(m);
+}
+
+static void
+test_map_iter_prefix(qb_map_t *m)
+{
+	void *data;
+	const char *p;
+	qb_map_iter_t *it;
+	int count;
+
+	qb_map_put(m, "aaaa", "aye");
+	qb_map_put(m, "facc", "nope");
+	qb_map_put(m, "abbb", "bee");
+	qb_map_put(m, "a.ac", "nope");
+	qb_map_put(m, "aacc", "yip");
+	qb_map_put(m, "cacc", "nope");
+	qb_map_put(m, "c", "----");
+
+	count = 0;
+	it = qb_map_pref_iter_create(m, "aa");
+	while ((p = qb_map_iter_next(it, &data)) != NULL) {
+		printf("1: %s == %s\n", p, (char*)data);
+		count++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_int_eq(count, 2);
+
+	count = 0;
+	it = qb_map_pref_iter_create(m, "a");
+	while ((p = qb_map_iter_next(it, &data)) != NULL) {
+		printf("2: %s == %s\n", p, (char*)data);
+		count++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_int_eq(count, 4);
+
+	count = 0;
+	it = qb_map_pref_iter_create(m, "zz");
+	while ((p = qb_map_iter_next(it, &data)) != NULL) {
+		printf("??: %s == %s\n", p, (char*)data);
+		count++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_int_eq(count, 0);
+
+	count = 0;
+	it = qb_map_pref_iter_create(m, "c");
+	while ((p = qb_map_iter_next(it, &data)) != NULL) {
+		printf("3: %s == %s\n", p, (char*)data);
+		count++;
+	}
+	qb_map_iter_free(it);
+	ck_assert_int_eq(count, 2);
+
+	qb_map_destroy(m);
+}
+
+
+static void
+test_map_traverse_unordered(qb_map_t *m)
+{
+	int32_t i;
+	srand(time(NULL));
+	for (i = 0; i < 30; i++) {
+		qb_map_put(m, chars[i], chars[i]);
+	}
+	qb_map_foreach(m, traverse_and_remove_func, m);
+	qb_map_destroy(m);
+}
+
+static int32_t
+my_counter_traverse(const char *key, void *value, void *data)
+{
+	int32_t *c = (int32_t*)data;
+	(*c)++;
+	return QB_FALSE;
+}
+
+static void
+test_map_load(qb_map_t *m, const char* test_name)
+{
+	char word[1000];
+	char *w;
+	FILE *fp;
+	int32_t res = 0;
+	int32_t count;
+	int32_t count2;
+	float ops;
+	float secs;
+	void *value;
+	qb_util_stopwatch_t *sw;
+
+	ck_assert(m != NULL);
+	sw = qb_util_stopwatch_create();
+
+#define MAX_WORDS 100000
+
+	/*
+	 * Load with dictionary
+	 */
+	fp = fopen("/usr/share/dict/words", "r");
+	qb_util_stopwatch_start(sw);
+	count = 0;
+	while (fgets(word, sizeof(word), fp) && count < MAX_WORDS) {
+		w = strdup(word);
+		qb_map_put(m, w, w);
+		count++;
+	}
+	qb_util_stopwatch_stop(sw);
+	ck_assert_int_eq(qb_map_count_get(m), count);
+	fclose(fp);
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops = (float)count / secs;
+	qb_log(LOG_INFO, "%25s %12.2f puts/sec (%d/%fs)\n", test_name, ops, count, secs);
+
+	/*
+	 * Verify dictionary produces correct values
+	 */
+	fp = fopen("/usr/share/dict/words", "r");
+	qb_util_stopwatch_start(sw);
+	count2 = 0;
+	while (fgets(word, sizeof(word), fp) && count2 < MAX_WORDS) {
+		value = qb_map_get(m, word);
+		ck_assert_str_eq(word, value);
+		count2++;
+	}
+	qb_util_stopwatch_stop(sw);
+	fclose(fp);
+
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops = (float)count2 / secs;
+	qb_log(LOG_INFO, "%25s %12.2f gets/sec (%d/%fs)\n", test_name, ops, count2, secs);
+
+	/*
+	 * time the iteration
+	 */
+	count2 = 0;
+	qb_util_stopwatch_start(sw);
+	qb_map_foreach(m, my_counter_traverse, &count2);
+	qb_util_stopwatch_stop(sw);
+	ck_assert_int_eq(qb_map_count_get(m), count2);
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops = (float)count2 / secs;
+	qb_log(LOG_INFO, "%25s %12.2f iters/sec (%d/%fs)\n", test_name, ops, count2, secs);
+
+	/*
+	 * Delete all dictionary entries
+	 */
+	fp = fopen("/usr/share/dict/words", "r");
+	qb_util_stopwatch_start(sw);
+	count2 = 0;
+	while (fgets(word, sizeof(word), fp) && count2 < MAX_WORDS) {
+		res = qb_map_rm(m, word);
+		ck_assert_int_eq(res, QB_TRUE);
+		count2++;
+	}
+	qb_util_stopwatch_stop(sw);
+	ck_assert_int_eq(qb_map_count_get(m), 0);
+	fclose(fp);
+
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops = (float)count2 / secs;
+	qb_log(LOG_INFO, "%25s %12.2f dels/sec (%d/%fs)\n", test_name, ops, count2, secs);
+}
+
+START_TEST(test_skiplist_simple)
+{
+	qb_map_t *m = qb_skiplist_create();
+	test_map_simple(m, __func__);
+}
+END_TEST
+
+START_TEST(test_hashtable_simple)
+{
+	qb_map_t *m = qb_hashtable_create(32);
+	test_map_simple(m, __func__);
+}
+END_TEST
+
+START_TEST(test_trie_simple)
+{
+	qb_map_t *m = qb_trie_create();
+	test_map_simple(m, __func__);
+}
+END_TEST
+
+START_TEST(test_skiplist_search)
+{
+	qb_map_t *m = qb_skiplist_create();
+	test_map_search(m);
+}
+END_TEST
+
+START_TEST(test_trie_search)
+{
+	qb_map_t *m = qb_trie_create();
+	test_map_search(m);
+}
+END_TEST
+
+START_TEST(test_skiplist_remove)
+{
+	qb_map_t *m = qb_skiplist_create();
+	test_map_remove(m);
+}
+END_TEST
+
+START_TEST(test_hashtable_remove)
+{
+	qb_map_t *m = qb_hashtable_create(256);
+	test_map_remove(m);
+}
+END_TEST
+
+START_TEST(test_trie_notifications)
+{
+	qb_map_t *m;
+	m = qb_trie_create();
+	test_map_remove(m);
+	m = qb_trie_create();
+	test_map_notifications_basic(m);
+	m = qb_trie_create();
+	test_map_notifications_prefix(m);
+	m = qb_trie_create();
+	test_map_notifications_free(m);
+	m = qb_trie_create();
+	test_map_notifications_iter(m);
+}
+END_TEST
+
+START_TEST(test_hash_notifications)
+{
+	qb_map_t *m;
+	m = qb_hashtable_create(256);
+	test_map_notifications_basic(m);
+	m = qb_hashtable_create(256);
+	test_map_notifications_free(m);
+}
+END_TEST
+
+START_TEST(test_skiplist_notifications)
+{
+	qb_map_t *m;
+	m = qb_skiplist_create();
+	test_map_notifications_basic(m);
+	m = qb_skiplist_create();
+	test_map_notifications_free(m);
+}
+END_TEST
+
+START_TEST(test_skiplist_traverse)
+{
+	qb_map_t *m;
+	m = qb_skiplist_create();
+	test_map_traverse_ordered(m);
+
+	m = qb_skiplist_create();
+	test_map_traverse_unordered(m);
+	m = qb_skiplist_create();
+	test_map_iter_safety(m, QB_TRUE);
+}
+END_TEST
+
+START_TEST(test_hashtable_traverse)
+{
+	qb_map_t *m;
+	m = qb_hashtable_create(256);
+	test_map_traverse_unordered(m);
+	m = qb_hashtable_create(256);
+	test_map_iter_safety(m, QB_FALSE);
+}
+END_TEST
+
+START_TEST(test_trie_traverse)
+{
+	qb_map_t *m;
+	m = qb_trie_create();
+	test_map_traverse_unordered(m);
+	m = qb_trie_create();
+	test_map_iter_safety(m, QB_FALSE);
+	m = qb_trie_create();
+	test_map_iter_prefix(m);
+}
+END_TEST
+
+START_TEST(test_skiplist_load)
+{
+	qb_map_t *m;
+	if (access("/usr/share/dict/words", R_OK) != 0) {
+		printf("no dict/words - not testing\n");
+		return;
+	}
+	m = qb_skiplist_create();
+	test_map_load(m, __func__);
+}
+END_TEST
+
+START_TEST(test_hashtable_load)
+{
+	qb_map_t *m;
+	if (access("/usr/share/dict/words", R_OK) != 0) {
+		printf("no dict/words - not testing\n");
+		return;
+	}
+	m = qb_hashtable_create(100000);
+	test_map_load(m, __func__);
+}
+END_TEST
+
+START_TEST(test_trie_load)
+{
+	qb_map_t *m;
+	if (access("/usr/share/dict/words", R_OK) != 0) {
+		printf("no dict/words - not testing\n");
+		return;
+	}
+	m = qb_trie_create();
+	test_map_load(m, __func__);
+}
+END_TEST
+
+/*
+ * From Honza: https://github.com/asalkeld/libqb/issues/44
+ */
+START_TEST(test_trie_partial_iterate)
+{
+        qb_map_t *map;
+        qb_map_iter_t *iter;
+        const char *res;
+        char *item;
+	int rc;
+
+        ck_assert((map = qb_trie_create()) != NULL);
+        qb_map_put(map, strdup("testobj.testkey"), strdup("one"));
+        qb_map_put(map, strdup("testobj.testkey2"), strdup("two"));
+
+        iter = qb_map_pref_iter_create(map, "testobj.");
+        ck_assert(iter != NULL);
+        res = qb_map_iter_next(iter, (void **)&item);
+        fprintf(stderr, "%s = %s\n", res, item);
+        qb_map_iter_free(iter);
+
+        item = qb_map_get(map, "testobj.testkey");
+        ck_assert_str_eq(item, "one");
+
+        rc = qb_map_rm(map, "testobj.testkey");
+        ck_assert(rc == QB_TRUE);
+
+        item = qb_map_get(map, "testobj.testkey");
+        ck_assert(item == NULL);
+
+}
+END_TEST
+
+
+static Suite *
+map_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("qb_map");
+
+	tc = tcase_create("skiplist_simple");
+	tcase_add_test(tc, test_skiplist_simple);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("hashtable_simple");
+	tcase_add_test(tc, test_hashtable_simple);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trie_simple");
+	tcase_add_test(tc, test_trie_simple);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trie_partial_iterate");
+	tcase_add_test(tc, test_trie_partial_iterate);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("skiplist_remove");
+	tcase_add_test(tc, test_skiplist_remove);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("hashtable_remove");
+	tcase_add_test(tc, test_hashtable_remove);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trie_notifications");
+	tcase_add_test(tc, test_trie_notifications);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("hash_notifications");
+	tcase_add_test(tc, test_hash_notifications);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("skiplist_notifications");
+	tcase_add_test(tc, test_skiplist_notifications);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("skiplist_search");
+	tcase_add_test(tc, test_skiplist_search);
+	suite_add_tcase(s, tc);
+
+/*
+ * 	No hashtable_search as it assumes an ordered
+ *	collection
+ */
+	tc = tcase_create("trie_search");
+	tcase_add_test(tc, test_trie_search);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("skiplist_traverse");
+	tcase_add_test(tc, test_skiplist_traverse);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("hashtable_traverse");
+	tcase_add_test(tc, test_hashtable_traverse);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trie_traverse");
+	tcase_add_test(tc, test_trie_traverse);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("skiplist_load");
+	tcase_add_test(tc, test_skiplist_load);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("hashtable_load");
+	tcase_add_test(tc, test_hashtable_load);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trie_load");
+	tcase_add_test(tc, test_trie_load);
+	tcase_set_timeout(tc, 30);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t
+main(void)
+{
+	int32_t number_failed;
+
+	Suite *s = map_suite();
+	SRunner *sr = srunner_create(s);
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_format_set(QB_LOG_STDERR, "%f:%l %p %b");
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_rb.c b/tests/check_rb.c
new file mode 100644
index 0000000..c77f360
--- /dev/null
+++ b/tests/check_rb.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbrb.h>
+#include <qb/qbipc_common.h>
+#include <qb/qblog.h>
+
+START_TEST(test_ring_buffer1)
+{
+	char my_buf[512];
+	struct qb_ipc_request_header hdr;
+	char *str;
+	qb_ringbuffer_t *rb;
+	int32_t i;
+	int32_t b;
+	ssize_t actual;
+	ssize_t avail;
+
+	rb = qb_rb_open("test1", 200, QB_RB_FLAG_CREATE, 0);
+	fail_if(rb == NULL);
+
+	for (b = 0; b < 3; b++) {
+		memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header));
+		str = my_buf + sizeof(struct qb_ipc_request_header);
+
+		for (i = 0; i < 900; i++) {
+			hdr.id = __LINE__ + i;
+			hdr.size =
+			    sprintf(str, "ID: %d (%s + i(%d)) -- %s-%s!",
+				    hdr.id, "actually the line number", i,
+				    __func__, __FILE__) + 1;
+			hdr.size += sizeof(struct qb_ipc_request_header);
+			memcpy(my_buf, &hdr, sizeof(struct qb_ipc_request_header));
+			avail = qb_rb_space_free(rb);
+			actual = qb_rb_chunk_write(rb, my_buf, hdr.size);
+			if (avail < (hdr.size + (3 * sizeof(uint32_t)))) {
+				ck_assert_int_eq(actual, -EAGAIN);
+			} else {
+				ck_assert_int_eq(actual, hdr.size);
+			}
+		}
+
+		memset(my_buf, 0, sizeof(my_buf));
+
+		memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header));
+		str = my_buf + sizeof(struct qb_ipc_request_header);
+
+		for (i = 0; i < 15; i++) {
+			actual = qb_rb_chunk_read(rb, my_buf, 512, 0);
+			if (actual < 0) {
+				ck_assert_int_eq(0, qb_rb_chunks_used(rb));
+				break;
+			}
+			memcpy(&hdr, my_buf, sizeof(struct qb_ipc_request_header));
+			str[actual - sizeof(struct qb_ipc_request_header)] = '\0';
+			ck_assert_int_eq(actual, hdr.size);
+		}
+	}
+	qb_rb_close(rb);
+}
+END_TEST
+
+/*
+ * nice size (int64)
+ */
+START_TEST(test_ring_buffer2)
+{
+	qb_ringbuffer_t *t;
+	int32_t i;
+	int64_t v = 7891034;
+	int64_t *new_data;
+	ssize_t l;
+
+	t = qb_rb_open("test2", 200 * sizeof(int64_t), QB_RB_FLAG_CREATE, 0);
+	fail_if(t == NULL);
+	for (i = 0; i < 200; i++) {
+		l = qb_rb_chunk_write(t, &v, sizeof(v));
+		ck_assert_int_eq(l, sizeof(v));
+	}
+	for (i = 0; i < 100; i++) {
+		l = qb_rb_chunk_peek(t, (void **)&new_data, 0);
+		ck_assert_int_eq(l, sizeof(v));
+		fail_unless(v == *new_data);
+		qb_rb_chunk_reclaim(t);
+	}
+	for (i = 0; i < 100; i++) {
+		l = qb_rb_chunk_write(t, &v, sizeof(v));
+		ck_assert_int_eq(l, sizeof(v));
+	}
+	for (i = 0; i < 100; i++) {
+		l = qb_rb_chunk_peek(t, (void **)&new_data, 0);
+		if (l == 0) {
+			/* no more to read */
+			break;
+		}
+		ck_assert_int_eq(l, sizeof(v));
+		fail_unless(v == *new_data);
+		qb_rb_chunk_reclaim(t);
+	}
+	qb_rb_close(t);
+}
+END_TEST
+
+/*
+ * odd size (10)
+ */
+START_TEST(test_ring_buffer3)
+{
+	qb_ringbuffer_t *t;
+	int32_t i;
+	char v[] = "1234567891";
+	char out[32];
+	ssize_t l;
+	size_t len = strlen(v) + 1;
+
+	t = qb_rb_open("test3", 10, QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
+	fail_if(t == NULL);
+	for (i = 0; i < 9000; i++) {
+		l = qb_rb_chunk_write(t, v, len);
+		ck_assert_int_eq(l, len);
+	}
+	for (i = 0; i < 2000; i++) {
+		l = qb_rb_chunk_read(t, (void *)out, 32, 0);
+		if (l < 0) {
+			/* no more to read */
+			break;
+		}
+		ck_assert_int_eq(l, len);
+		ck_assert_str_eq(v, out);
+	}
+	qb_rb_close(t);
+}
+END_TEST
+
+START_TEST(test_ring_buffer4)
+{
+	qb_ringbuffer_t *t;
+	char data[] = "1234567891";
+	int32_t i;
+	char *new_data;
+	ssize_t l;
+
+	t = qb_rb_open("test4", 10, QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
+	fail_if(t == NULL);
+	for (i = 0; i < 2000; i++) {
+		l = qb_rb_chunk_write(t, data, strlen(data));
+		ck_assert_int_eq(l, strlen(data));
+		if (i == 0) {
+			data[0] = 'b';
+		}
+	}
+	for (i = 0; i < 2000; i++) {
+		l = qb_rb_chunk_peek(t, (void **)&new_data, 0);
+		if (l == 0) {
+			break;
+		}
+		ck_assert_int_eq(l, strlen(data));
+		qb_rb_chunk_reclaim(t);
+	}
+	qb_rb_close(t);
+}
+END_TEST
+
+static Suite *rb_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("ringbuffer");
+
+	tc = tcase_create("test01");
+	tcase_add_test(tc, test_ring_buffer1);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("test02");
+	tcase_add_test(tc, test_ring_buffer2);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("test03");
+	tcase_add_test(tc, test_ring_buffer3);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("test04");
+	tcase_add_test(tc, test_ring_buffer4);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t main(void)
+{
+	int32_t number_failed;
+
+	Suite *s = rb_suite();
+	SRunner *sr = srunner_create(s);
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/check_util.c b/tests/check_util.c
new file mode 100644
index 0000000..a948739
--- /dev/null
+++ b/tests/check_util.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake <sdake at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+#include <check.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+#define assert_int_between(_c, _lower, _upper) \
+_ck_assert_int(_c, >=, _lower); \
+_ck_assert_int(_c, <=, _upper);
+
+
+START_TEST(test_check_overwrite)
+{
+	uint64_t res;
+	uint32_t last;
+	qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+
+	qb_util_stopwatch_split_ctl(sw, 5, QB_UTIL_SW_OVERWRITE);
+
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 0, 100);
+
+	usleep(10000);
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 9000, 11000);
+
+	usleep(20000);
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 19000, 21000);
+
+	usleep(30000);
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 29000, 31000);
+
+	usleep(40000);
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 39000, 41000);
+
+	/*
+	 * window should be 100000 (40000 + 30000 + 20000 + 10000) usec
+	 */
+	last = qb_util_stopwatch_split_last(sw);
+	res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
+	assert_int_between(res, 95000, 105000);
+
+	usleep(50000);
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 49000, 52000);
+	/*
+	 * window should be 140000 (50000 + 40000 + 30000 + 20000) usec
+	 */
+	last = qb_util_stopwatch_split_last(sw);
+	res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
+	assert_int_between(res, 135000, 145000);
+
+	usleep(25000);
+	qb_util_stopwatch_split(sw);
+
+	/* ask for a split that has been overwritten.
+	 */
+	res = qb_util_stopwatch_time_split_get(sw, last, 1);
+	ck_assert_int_eq(res, 0);
+
+	/* iterating
+	 */
+	last = qb_util_stopwatch_split_last(sw);
+	do {
+		res = qb_util_stopwatch_time_split_get(sw, last, last);
+		qb_log(LOG_INFO, "overwrite split %d is %"PRIu64"", last, res);
+		last--;
+	} while (res > 0);
+
+	qb_util_stopwatch_free(sw);
+}
+END_TEST
+
+START_TEST(test_check_normal)
+{
+	uint64_t res;
+	uint32_t last;
+	qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+
+	qb_util_stopwatch_split_ctl(sw, 3, 0);
+
+	qb_util_stopwatch_start(sw);
+	usleep(33000);
+	/* 1 */
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 30000, 36000);
+	last = qb_util_stopwatch_split_last(sw);
+	ck_assert_int_eq(last, 0);
+
+	usleep(10000);
+	/* 2 */
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 9000, 11000);
+
+	usleep(20000);
+	/* 3 */
+	res = qb_util_stopwatch_split(sw);
+	assert_int_between(res, 19000, 21000);
+
+	/* no more space */
+	res = qb_util_stopwatch_split(sw);
+	ck_assert_int_eq(res, 0);
+
+	/*
+	 * split should be 30000 (10000 + 20000) usec
+	 */
+	last = qb_util_stopwatch_split_last(sw);
+	ck_assert_int_eq(last, 2);
+	res = qb_util_stopwatch_time_split_get(sw, last, 0);
+	assert_int_between(res, 25000, 35000);
+
+	/* ask for a split that has beyond the max.
+	 */
+	res = qb_util_stopwatch_time_split_get(sw, 3, 2);
+	ck_assert_int_eq(res, 0);
+
+	/* iterating
+	 */
+	last = qb_util_stopwatch_split_last(sw);
+	do {
+		res = qb_util_stopwatch_time_split_get(sw, last, last);
+		qb_log(LOG_INFO, "normal split %d is %"PRIu64"", last, res);
+		last--;
+	} while (res > 0);
+
+	qb_util_stopwatch_free(sw);
+}
+END_TEST
+
+static Suite *util_suite(void)
+{
+	TCase *tc;
+	Suite *s = suite_create("qb_util");
+
+	tc = tcase_create("overwrite");
+	tcase_add_test(tc, test_check_overwrite);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("normal");
+	tcase_add_test(tc, test_check_normal);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
+
+int32_t main(void)
+{
+	int32_t number_failed;
+
+	Suite *s = util_suite();
+	SRunner *sr = srunner_create(s);
+
+	qb_log_init("check", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	srunner_run_all(sr, CK_VERBOSE);
+	number_failed = srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/crash_test_dummy.c b/tests/crash_test_dummy.c
new file mode 100644
index 0000000..9356571
--- /dev/null
+++ b/tests/crash_test_dummy.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <signal.h>
+#include <syslog.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+
+
+static void
+func_one(void)
+{
+	FILE *fd;
+
+	qb_enter();
+	qb_log(LOG_DEBUG, "arf arf?");
+	qb_log(LOG_CRIT, "arrrg!");
+	qb_log(134, "big priority");
+	qb_log(LOG_ERR, "oops, I did it again");
+	qb_log(LOG_INFO, "are you aware ...");
+
+	fd = fopen("/nothing.txt", "r+");
+	if (fd == NULL) {
+		qb_perror(LOG_ERR, "can't open(\"/nothing.txt\")");
+	} else {
+		fclose(fd);
+	}
+	qb_leave();
+}
+
+static void
+func_two(void)
+{
+	qb_enter();
+	qb_logt(LOG_DEBUG, 0, "arf arf?");
+	qb_log(LOG_CRIT, "arrrg!");
+	qb_log(LOG_ERR, "oops, I did it again");
+	qb_log(LOG_INFO, "are you aware ...");
+	qb_leave();
+}
+
+
+static void
+sigsegv_handler(int sig)
+{
+	(void)signal(SIGSEGV, SIG_DFL);
+	qb_log_blackbox_write_to_file("crash-test-dummy.fdata");
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
+	raise(SIGSEGV);
+}
+
+
+int32_t
+main(int32_t argc, char *argv[])
+{
+	char *logfile;
+	int i;
+
+	signal(SIGSEGV, sigsegv_handler);
+
+	qb_log_init("crash-test-dummy", LOG_USER, LOG_INFO);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+
+	qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 4096);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_THREADED, QB_FALSE);
+	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	for (i = 0; i < 1000; i++) {
+		qb_log(LOG_DEBUG, "hello");
+		qb_log(LOG_INFO, "this is an info");
+		qb_log(LOG_NOTICE, "hello - notice?");
+		{
+			char * str = NULL;
+			qb_log(LOG_ERR,
+			       "%s-%d-%s-%u",
+			       NULL, 952, str, 56);
+		}
+		func_one();
+		func_two();
+	}
+
+	/* on purpose crash to make a blackbox.
+	 */
+       	logfile = NULL;
+	logfile[5] = 'a';
+	return 0;
+}
diff --git a/tests/file_change_bytes.c b/tests/file_change_bytes.c
new file mode 100644
index 0000000..a725278
--- /dev/null
+++ b/tests/file_change_bytes.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse <jfriesse at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static void usage(void)
+{
+	printf("Usage: [ -i input_file] [ -o output_file ] [ -n no_bytes]\n");
+	printf("Changes no_bytes (default 1024) in input_file (default = - = stdin) and store\n");
+	printf("result to output_file (default = - = stdout). It's possible to use same file\n");
+	printf("as both input and output\n");
+
+	exit(1);
+}
+
+static void init_rand(void) {
+	unsigned int init_v;
+
+	init_v = time(NULL) + getpid();
+
+	srand(init_v);
+}
+
+int main(int argc, char *argv[])
+{
+	FILE *fi, *fo;
+	int i;
+	const char *input_file_name;
+	const char *output_file_name;
+	int no_bytes;
+	char *ep;
+	int ch;
+	unsigned char *data;
+	size_t data_size;
+	size_t data_pos;
+	size_t input_data_size;
+	unsigned char buf[1024];
+
+	input_file_name = "-";
+	output_file_name = "-";
+	no_bytes = 1024;
+
+	while ((ch = getopt(argc, argv, "hi:o:n:")) != -1) {
+		switch (ch) {
+		case 'i':
+			input_file_name = optarg;
+			break;
+		case 'n':
+			no_bytes = strtol(optarg, &ep, 10);
+			if (no_bytes < 0 || *ep != '\0') {
+				warnx("illegal number -- %s", argv[2]);
+				usage();
+			}
+			break;
+		case 'o':
+			output_file_name = optarg;
+			break;
+		case 'h':
+		case '?':
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+
+	if (strcmp(input_file_name, "-") == 0) {
+		fi = stdin;
+	} else {
+		fi = fopen(input_file_name, "rb");
+		if (fi == NULL) {
+			err(1, "%s", input_file_name);
+		}
+	}
+
+	/*
+	 * Open and fully read input file
+	 */
+	data = NULL;
+	data_size = 0;
+	data_pos = 0;
+	while ((input_data_size = fread(buf, 1, sizeof(buf), fi)) != 0) {
+		if (data_pos + input_data_size >= data_size) {
+			data_size = (data_size + input_data_size) * 2;
+			assert((data = realloc(data, data_size)) != NULL);
+		}
+		memcpy(data + data_pos, buf, input_data_size);
+		data_pos += input_data_size;
+	}
+	fclose(fi);
+
+	/*
+	 * Change bytes
+	 */
+	init_rand();
+
+	for (i = 0; i < no_bytes; i++) {
+		data[rand() % data_pos] = rand();
+	}
+
+	/*
+	 * Fully write ouput file
+	 */
+	if (strcmp(output_file_name, "-") == 0) {
+		fo = stdout;
+	} else {
+		fo = fopen(output_file_name, "wb");
+		if (fo == NULL) {
+			err(1, "%s", output_file_name);
+		}
+	}
+	assert(fwrite(data, 1, data_pos, fo) == data_pos);
+	fclose(fo);
+
+	free(data);
+
+	return (0);
+}
diff --git a/tests/format_compare_speed.c b/tests/format_compare_speed.c
new file mode 100644
index 0000000..1b04bd7
--- /dev/null
+++ b/tests/format_compare_speed.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "os_base.h"
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap);
+
+static void
+store_this_qb(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+static void
+store_this_snprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+typedef void (*snprintf_like_func)(const char *fmt, ...)  __attribute__((format(printf, 1, 2)));
+
+
+static void
+store_this_qb(const char *fmt, ...)
+{
+	char buf[QB_LOG_MAX_LEN];
+	va_list ap;
+
+	va_start(ap, fmt);
+	qb_vsnprintf_serialize(buf, QB_LOG_MAX_LEN, fmt, ap);
+	va_end(ap);
+}
+
+static void
+store_this_snprintf(const char *fmt, ...)
+{
+	char buf[QB_LOG_MAX_LEN];
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, QB_LOG_MAX_LEN, fmt, ap);
+	va_end(ap);
+}
+
+#define ITERATIONS 10000000
+
+static void
+test_this_one(const char *name, snprintf_like_func func)
+{
+	int i;
+	qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
+	float elapsed = 452.245252343;
+	float ops_per_sec = 0.345624523;
+
+	qb_util_stopwatch_start(sw);
+	for (i = 0; i < ITERATIONS; i++) {
+		func("%d %s %llu %9.3f", i, "hello", 3425ULL, elapsed);
+		func("[%10s] %.32x -> %p", "hello", i, func);
+		func("Client %s.%.9s wants to fence (%s) '%s' with device '%3.5f'",
+		     "bla", "foooooooooooooooooo",
+		     name, "target", ops_per_sec);
+		func("Node %s now has process list: %.32x (was %.32x)",
+		     "18builder", 2, 0);
+	}
+	qb_util_stopwatch_stop(sw);
+	elapsed = qb_util_stopwatch_sec_elapsed_get(sw);
+	ops_per_sec = ((float)ITERATIONS) / elapsed;
+	printf("%s] Duration: %9.3f OPs/sec: %9.3f\n", name, elapsed, ops_per_sec);
+	qb_util_stopwatch_free(sw);
+}
+
+int
+main(void)
+{
+	test_this_one("qb store", store_this_qb);
+	test_this_one("snprintf", store_this_snprintf);
+
+	return 0;
+}
diff --git a/tests/loop.c b/tests/loop.c
new file mode 100644
index 0000000..1a6d7dd
--- /dev/null
+++ b/tests/loop.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * This file is part of libqb.
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "os_base.h"
+#include <sys/poll.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+#include <qb/qbloop.h>
+
+static struct qb_loop *l;
+static qb_loop_timer_handle th;
+
+static void job_3_9(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_1_2(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_2_4(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_3_5(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_3_6(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_1_1(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_3_7(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_2_3(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_2_8(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+static void job_1_9(void *data) { qb_log(LOG_INFO, "%s\n", __func__); }
+
+static void more_important_jobs(void *data)
+{
+	qb_enter();
+	qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1_2);
+	qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1_9);
+}
+
+static int32_t handle_reconf_signal(int32_t sig, void *data)
+{
+	qb_log(LOG_INFO, "signal %d", sig);
+	return 0;
+}
+
+static int32_t handle_exit_signal(int32_t sig, void *data)
+{
+	qb_log(LOG_INFO, "exiting (signal %d)... bye", sig);
+	qb_loop_stop(l);
+	return -1;
+}
+
+static void more_jobs(void *data)
+{
+	qb_log(LOG_INFO, "%s\n", __func__);
+	qb_loop_timer_add(l, QB_LOOP_HIGH, 3109*QB_TIME_NS_IN_MSEC, NULL, job_1_1, &th);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_7);
+	qb_loop_timer_add(l, QB_LOOP_LOW, 1000*QB_TIME_NS_IN_MSEC, NULL, more_important_jobs, &th);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_7);
+	qb_loop_timer_add(l, QB_LOOP_LOW, 2341*QB_TIME_NS_IN_MSEC, NULL, job_3_7, &th);
+	qb_loop_timer_add(l, QB_LOOP_LOW, 900, NULL, job_3_6, &th);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_5);
+	qb_loop_timer_add(l, QB_LOOP_MED, 4000*QB_TIME_NS_IN_MSEC, NULL, more_jobs, &th);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_9);
+	qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1_9);
+	qb_loop_job_add(l, QB_LOOP_MED,  NULL, job_2_3);
+}
+
+static int32_t read_stdin(int32_t fd, int32_t revents, void *data)
+{
+	char buf[100];
+	ssize_t len = read(fd, buf, 100);
+	buf[len-1] = '\0';
+	qb_log(LOG_INFO, "typed > \"%s\"\n", buf);
+	if (strcmp(buf, "more") == 0) {
+		more_jobs(NULL);
+	}
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_9);
+	return 0;
+}
+
+int main(int argc, char * argv[])
+{
+	qb_loop_signal_handle sh;
+
+	qb_log_init("loop", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	l = qb_loop_create();
+
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_9);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_2_4);
+	qb_loop_job_add(l, QB_LOOP_HIGH, NULL, job_1_2);
+	qb_loop_job_add(l, QB_LOOP_MED,  NULL, job_3_7);
+/*
+ * 	qb_loop_timer_add(l, QB_LOOP_HIGH, 40*QB_TIME_NS_IN_MSEC, NULL, more_jobs, &th);
+ */
+	qb_loop_job_add(l, QB_LOOP_MED,  NULL, job_2_8);
+	qb_loop_job_add(l, QB_LOOP_LOW,  NULL, job_3_6);
+
+	qb_loop_poll_add(l, QB_LOOP_LOW, 0, POLLIN | POLLPRI | POLLNVAL,
+			     NULL, read_stdin);
+
+	qb_loop_signal_add(l, QB_LOOP_MED, SIGINT, NULL, handle_exit_signal, &sh);
+	qb_loop_signal_add(l, QB_LOOP_MED, SIGSEGV, NULL, handle_exit_signal, &sh);
+	qb_loop_signal_add(l, QB_LOOP_MED, SIGHUP, NULL, handle_reconf_signal, &sh);
+
+	qb_loop_run(l);
+	return 0;
+}
+
+
diff --git a/tests/make-log-test.sh b/tests/make-log-test.sh
new file mode 100755
index 0000000..0dba275
--- /dev/null
+++ b/tests/make-log-test.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+line=
+count=0
+total=50000
+echo "#include <stdio.h>"
+echo "#include <qb/qblog.h>"
+echo "extern void log_dict_words(void);"
+echo "void log_dict_words(void) {"
+
+while read  w
+do
+	if [ $count -eq 0 ]
+	then
+		line="    qb_log(LOG_DEBUG, \"%d : %s %s %s\", $total"
+	fi
+	line="$line, \"$w\""
+	let count="$count+1"
+	if [ $count -eq 3 ]
+	then
+		line="$line );"
+		count=0
+		let total="$total-1"
+		echo $line
+		if [ $total -eq 0 ]
+		then
+			echo "}"
+			exit 0
+		fi
+	fi
+done < /usr/share/dict/words
+
+echo "}"
+
diff --git a/tests/rbreader.c b/tests/rbreader.c
new file mode 100644
index 0000000..406bbd8
--- /dev/null
+++ b/tests/rbreader.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Angus Salkeld <asalkeld at redhat.com>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qbrb.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+#define ONE_MEG 1048576
+static qb_ringbuffer_t *rb = NULL;
+static int keep_reading = QB_TRUE;
+int8_t buffer[ONE_MEG];
+
+
+static void sigterm_handler(int32_t num)
+{
+	qb_log(LOG_INFO, "signal %d", num);
+	keep_reading = QB_FALSE;
+}
+
+int32_t main(int32_t argc, char *argv[])
+{
+	ssize_t num_read;
+
+	signal(SIGINT, sigterm_handler);
+
+	qb_log_init("rbreader", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	rb = qb_rb_open("tester", ONE_MEG * 3,
+			QB_RB_FLAG_SHARED_PROCESS |
+			QB_RB_FLAG_CREATE, 0);
+
+	if (rb == NULL) {
+		qb_perror(LOG_ERR, "failed to create ringbuffer");
+		return -1;
+	}
+	while (keep_reading) {
+		num_read = qb_rb_chunk_read(rb, buffer,
+					    ONE_MEG, 0);
+		if (num_read == -ETIMEDOUT) {
+			usleep(100000);
+		} else if (num_read < 0) {
+			errno = -num_read;
+			qb_perror(LOG_ERR, "nothing to read");
+		}
+	}
+	qb_rb_close(rb);
+	return 0;
+}
diff --git a/tests/rbwriter.c b/tests/rbwriter.c
new file mode 100644
index 0000000..4e684a0
--- /dev/null
+++ b/tests/rbwriter.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Steven Dake (sdake at redhat.com)
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <qb/qbipcc.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <qb/qbrb.h>
+#include <qb/qbdefs.h>
+#include <qb/qbutil.h>
+#include <qb/qblog.h>
+
+static int alarm_notice = 0;
+static qb_ringbuffer_t *rb = NULL;
+static qb_util_stopwatch_t *sw;
+#define ONE_MEG 1048576
+static char buffer[ONE_MEG * 3];
+
+static void sigalrm_handler (int num)
+{
+	alarm_notice = 1;
+}
+
+static void sigterm_handler(int32_t num)
+{
+	qb_log(LOG_INFO, "writer: %s(%d)\n", __func__, num);
+	qb_rb_close(rb);
+	exit(0);
+}
+
+static void
+_benchmark(ssize_t write_size)
+{
+	ssize_t res;
+	int write_count = 0;
+	float secs;
+
+	alarm_notice = 0;
+
+	alarm (10);
+
+	qb_util_stopwatch_start(sw);
+	do {
+		res = qb_rb_chunk_write(rb, buffer, write_size);
+		if (res == write_size) {
+			write_count++;
+		}
+	} while (alarm_notice == 0 && (res == write_size || res == -EAGAIN));
+	if (res < 0) {
+		perror("qb_ipcc_sendv");
+	}
+	qb_util_stopwatch_stop(sw);
+	secs = qb_util_stopwatch_sec_elapsed_get(sw);
+
+	printf ("%5d messages sent ", write_count);
+	printf ("%5ld bytes per write ", (long int) write_size);
+	printf ("%7.3f Seconds runtime ", secs);
+	printf ("%9.3f TP/s ",
+		((float)write_count) / secs);
+	printf ("%7.3f MB/s.\n",
+		((float)write_count) * ((float)write_size) / secs);
+}
+
+
+static void
+do_throughput_benchmark(void)
+{
+	ssize_t size = 64;
+	int i;
+
+	signal (SIGALRM, sigalrm_handler);
+	sw =  qb_util_stopwatch_create();
+
+	for (i = 0; i < 10; i++) { /* number of repetitions - up to 50k */
+		_benchmark(size);
+		signal (SIGALRM, sigalrm_handler);
+		size *= 5;
+		if (size >= ONE_MEG) {
+			break;
+		}
+	}
+}
+
+static void show_usage(const char *name)
+{
+	printf("usage: \n");
+	printf("%s <options>\n", name);
+	printf("\n");
+	printf("  options:\n");
+	printf("\n");
+	printf("  -n             non-blocking ipc (default blocking)\n");
+	printf("  -v             verbose\n");
+	printf("  -h             show this help text\n");
+	printf("\n");
+}
+
+int32_t main(int32_t argc, char *argv[])
+{
+	const char *options = "vh";
+	int32_t opt;
+	int32_t verbose = 0;
+
+	while ((opt = getopt(argc, argv, options)) != -1) {
+		switch (opt) {
+		case 'v':
+			verbose++;
+			break;
+		case 'h':
+		default:
+			show_usage(argv[0]);
+			exit(0);
+			break;
+		}
+	}
+
+	signal(SIGINT, sigterm_handler);
+
+	qb_log_init("rbwriter", LOG_USER, LOG_EMERG);
+	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
+			  QB_LOG_FILTER_FILE, "*", LOG_INFO + verbose);
+	qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+	rb = qb_rb_open("tester", ONE_MEG * 3,
+			QB_RB_FLAG_SHARED_PROCESS, 0);
+	do_throughput_benchmark();
+	qb_rb_close(rb);
+	return EXIT_SUCCESS;
+}
diff --git a/tests/resources.test b/tests/resources.test
new file mode 100755
index 0000000..4a1de14
--- /dev/null
+++ b/tests/resources.test
@@ -0,0 +1,22 @@
+#!/bin/sh
+RETURN=0
+
+for d in /dev/shm /var/run
+do
+	ls $d/qb-test* 2>/dev/null
+	if [ $? -eq 0 ]
+	then
+		echo
+		echo "Error: shared memory segments not closed/unlinked"
+		echo
+		RETURN=1
+	fi
+done
+ps aux | grep -v grep | grep lt-check
+if [ $? -eq 0 ]
+then
+	echo "test program frozen"
+	RETURN=1
+fi
+
+exit $RETURN
diff --git a/tests/test.conf.in b/tests/test.conf.in
new file mode 100644
index 0000000..ec747c9
--- /dev/null
+++ b/tests/test.conf.in
@@ -0,0 +1,6 @@
+if [ -z "@HAVE_SLOW_TESTS@" ]
+then
+	export NUM_BB_TESTS=32
+else
+	export NUM_BB_TESTS=1024
+fi
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..0a87671
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1 @@
+qb-blackbox
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..a790fd9
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,28 @@
+# Copyright (c) 2011 Red Hat, Inc.
+#
+# Authors: Angus Salkeld <asalkeld at redhat.com>
+#
+# This file is part of libqb.
+#
+# libqb is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# libqb 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+#
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST =
+CLEANFILES =
+
+sbin_PROGRAMS = qb-blackbox
+
+qb_blackbox_SOURCES = qb_blackbox.c $(top_builddir)/include/qb/qblog.h
+qb_blackbox_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+qb_blackbox_LDADD = $(top_builddir)/lib/libqb.la
diff --git a/tools/qb_blackbox.c b/tools/qb_blackbox.c
new file mode 100644
index 0000000..e39f6bf
--- /dev/null
+++ b/tools/qb_blackbox.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 Andrew Beekhof <andrew at beekhof.net>
+ *
+ * libqb is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * libqb 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <qb/qblog.h>
+
+int
+main(int argc, char **argv)
+{
+	int lpc = 0;
+
+        qb_log_init("qb_blackbox", LOG_USER, LOG_TRACE);
+        qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+        qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+
+	for(lpc = 1; lpc < argc && argv[lpc] != NULL; lpc++) {
+		printf("Dumping the contents of %s\n", argv[lpc]);
+		qb_log_blackbox_print_from_file(argv[lpc]);
+	}
+	return 0;
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-ha/libqb.git



More information about the Debian-HA-Commits mailing list