[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(¤t->list, &s->connections)) {
+ return NULL;
+ }
+
+ c = qb_list_first_entry(¤t->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(§->list);
+
+ pthread_rwlock_wrlock(&_listlock);
+ qb_list_add(§->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, ×tamp, 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(×tamp, ptr, sizeof(time_t));
+ ptr += sizeof(time_t);
+ tm = localtime(×tamp);
+ 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(¤t_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