pax_global_header00006660000000000000000000000064121742651620014520gustar00rootroot0000000000000052 comment=75f7ed373758b3cb9087e89e4fae17379dd7b483 libqb-0.16.0/000077500000000000000000000000001217426516200126755ustar00rootroot00000000000000libqb-0.16.0/.gitignore000066400000000000000000000003441217426516200146660ustar00rootroot00000000000000*.[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 *~ libqb-0.16.0/.travis.yml000066400000000000000000000006221217426516200150060ustar00rootroot00000000000000language: 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@lists.fedorahosted.org libqb-0.16.0/COPYING000066400000000000000000000576461217426516200137520ustar00rootroot00000000000000 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 libqb-0.16.0/INSTALL000066400000000000000000000033321217426516200137270ustar00rootroot00000000000000---------------------------------------------- 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 libqb-0.16.0/Lindent000077500000000000000000000014301217426516200142160ustar00rootroot00000000000000#!/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 "$@" libqb-0.16.0/Makefile.am000066400000000000000000000074451217426516200147430ustar00rootroot00000000000000# Copyright (C) 2010 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake # Angus Salkeld # # 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 . 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 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*' 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 libqb-0.16.0/README.markdown000066400000000000000000000031341217426516200153770ustar00rootroot00000000000000# 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://libqb.org/html/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. libqb-0.16.0/autogen.sh000077500000000000000000000002031217426516200146710ustar00rootroot00000000000000#!/bin/sh # Run this to generate all the initial makefiles, etc. mkdir -p m4 autoreconf -i -v && echo Now run ./configure and make libqb-0.16.0/build-aux/000077500000000000000000000000001217426516200145675ustar00rootroot00000000000000libqb-0.16.0/build-aux/.gitignore000066400000000000000000000001051217426516200165530ustar00rootroot00000000000000compile config.guess config.sub depcomp install-sh ltmain.sh missing libqb-0.16.0/build-aux/abi-check-templ.xml000066400000000000000000000002771217426516200202440ustar00rootroot00000000000000 @VERSION@ @PREFIX@/usr/include/qb @PREFIX@/usr/lib64/libqb.so libqb-0.16.0/build-aux/api-auto-test.xml000066400000000000000000000001341217426516200200030ustar00rootroot00000000000000 master include/qb lib/ libqb-0.16.0/build-aux/git-version-gen000077500000000000000000000146041217426516200175370ustar00rootroot00000000000000#!/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 . # 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@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*' HEAD 2>/dev/null \ || git describe --abbrev=4 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: libqb-0.16.0/build-aux/gitlog-to-changelog000077500000000000000000000126451217426516200203570ustar00rootroot00000000000000eval '(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 . # 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 < 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 = ) 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 = ) 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: libqb-0.16.0/build-aux/release.mk000066400000000000000000000026651217426516200165510ustar00rootroot00000000000000# to build official release tarballs, handle tagging and publish. # signing key gpgsignkey=C38157D2 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-* libqb-0.16.0/check000077500000000000000000000142751217426516200137110ustar00rootroot00000000000000#!/bin/bash export CFLAGS="$CFLAGS" export MAKEFLAGS="$MAKEFLAGS --no-print-directory" help_all() { echo echo "Usage: check " 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/lib64/clang-analyzer/scan-build/ccc-analyzer ] then echo try installing clang-analyze exit 1 fi echo "checking clang" echo "====================" make clean ./configure \ CC=/usr/lib64/clang-analyzer/scan-build/ccc-analyzer \ CXX=/usr/lib64/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 libqb-0.16.0/coding_style.txt000066400000000000000000000012221217426516200161160ustar00rootroot00000000000000 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 " 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 libqb-0.16.0/configure.ac000066400000000000000000000433371217426516200151750ustar00rootroot00000000000000 # 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@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 ]) AC_MSG_CHECKING(looking for union semun in sys/sem.h) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include #include #include ]], [[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 ], [ 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 ], [ 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= -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}]) libqb-0.16.0/docs/000077500000000000000000000000001217426516200136255ustar00rootroot00000000000000libqb-0.16.0/docs/.gitignore000066400000000000000000000000201217426516200156050ustar00rootroot00000000000000man3 html *.dox libqb-0.16.0/docs/Makefile.am000066400000000000000000000026501217426516200156640ustar00rootroot00000000000000# Copyright (C) 2010 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . 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 libqb-0.16.0/docs/html.dox.in000066400000000000000000000144651217426516200157240ustar00rootroot00000000000000DOXYFILE_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 libqb-0.16.0/docs/mainpage.h000066400000000000000000000056431217426516200155670ustar00rootroot00000000000000/** * @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 */ libqb-0.16.0/docs/man.dox.in000066400000000000000000000115021217426516200155200ustar00rootroot00000000000000DOXYFILE_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 libqb-0.16.0/docs/man8/000077500000000000000000000000001217426516200144705ustar00rootroot00000000000000libqb-0.16.0/docs/man8/qb-blackbox.8000066400000000000000000000040071217426516200167470ustar00rootroot00000000000000.\"/* .\" * Copyright (C) 2012 Red Hat, Inc. .\" * .\" * Author: Angus Salkeld .\" * .\" * 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 . .\" */ .TH QB-BLACKBOX 8 2012-05-28 .SH NAME qb-blackbox \- Display "flight data" from the "blackbox". .SH SYNOPSIS .B "qb-blackbox ..." .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 libqb-0.16.0/examples/000077500000000000000000000000001217426516200145135ustar00rootroot00000000000000libqb-0.16.0/examples/.gitignore000066400000000000000000000000741217426516200165040ustar00rootroot00000000000000simplelog tcpclient tcpserver ipcclient ipcserver mapnotify libqb-0.16.0/examples/Makefile.am000066400000000000000000000041351217426516200165520ustar00rootroot00000000000000# Copyright (c) 2011 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . # 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) libqb-0.16.0/examples/ipcclient.c000066400000000000000000000123731217426516200166370ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include 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 #define MAX_MSG_SIZE ONE_MEG static char data[ONE_MEG]; 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 \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 = "eb"; 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); conn = qb_ipcc_connect("ipcserver", MAX_MSG_SIZE); if (conn == NULL) { perror("qb_ipcc_connect"); exit(1); } if (do_benchmark) { do_throughput_benchmark(conn); } else { do_echo(conn); } qb_ipcc_disconnect(conn); return EXIT_SUCCESS; } libqb-0.16.0/examples/ipcserver.c000066400000000000000000000223321217426516200166630ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include #include #ifdef HAVE_GLIB #include static GMainLoop *glib_loop; static qb_array_t *gio_map; #endif /* HAVE_GLIB */ 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 \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); } 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; } libqb-0.16.0/examples/mapnotify.c000066400000000000000000000226631217426516200166760ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse * Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include 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); } libqb-0.16.0/examples/simplelog.c000066400000000000000000000143011217426516200166510ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #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 \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 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; } libqb-0.16.0/examples/tcpclient.c000066400000000000000000000043601217426516200166470ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #ifdef HAVE_NETDB_H #include #endif /* HAVE_NETDB_H */ #ifdef HAVE_SYS_SOCKET_H #include #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; } libqb-0.16.0/examples/tcpserver.c000066400000000000000000000072061217426516200167010ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ #ifdef HAVE_ARPA_INET_H #include #endif /* HAVE_ARPA_INET_H */ #ifdef HAVE_NETDB_H #include #endif /* HAVE_NETDB_H */ #ifdef HAVE_SYS_SOCKET_H #include #endif /* HAVE_SYS_SOCKET_H */ #ifdef HAVE_SYS_POLL_H #include #endif /* HAVE_SYS_POLL_H */ #include #include 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; } libqb-0.16.0/include/000077500000000000000000000000001217426516200143205ustar00rootroot00000000000000libqb-0.16.0/include/.gitignore000066400000000000000000000000701217426516200163050ustar00rootroot00000000000000config.h config.h.in qb/qbconfig.h qb/stamp-h2 stamp-h1 libqb-0.16.0/include/Makefile.am000066400000000000000000000016061217426516200163570ustar00rootroot00000000000000# Copyright (c) 2010 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . # MAINTAINERCLEANFILES = Makefile.in config.h.in EXTRA_DIST = $(noinst_HEADERS) noinst_HEADERS = os_base.h tlist.h SUBDIRS = qb libqb-0.16.0/include/os_base.h000066400000000000000000000104171217426516200161070ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_OS_BASE_H_DEFINED #define QB_OS_BASE_H_DEFINED #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif /* HAVE_SYS_TYPES_H */ #ifdef HAVE_SYS_UIO_H #include #endif /* HAVE_SYS_UIO_H */ #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #ifdef HAVE_STDDEF_H #include #endif /* HAVE_STDDEF_H */ #ifdef HAVE_INTTYPES_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #ifdef HAVE_STRING_H #include #endif /* HAVE_STRING_H */ #ifdef HAVE_STRINGS_H #include #endif /* HAVE_STRINGS_H */ #ifndef S_SPLINT_S #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #endif /* S_SPLINT_S */ #ifdef HAVE_ERRNO_H #include #endif /*HAVE_ERRNO_H*/ #ifdef HAVE_TIME_H #include #endif /* HAVE_TIME_H */ #ifdef HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ #ifdef HAVE_SYS_STAT_H #include #endif /* HAVE_SYS_STAT_H */ #ifdef HAVE_FCNTL_H #include #endif /* HAVE_FCNTL_H */ #ifdef HAVE_SYS_SOCKET_H #include #endif /* HAVE_SYS_SOCKET_H */ #ifndef S_SPLINT_S #ifdef HAVE_SYSLOG_H #include #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 */ libqb-0.16.0/include/qb/000077500000000000000000000000001217426516200147225ustar00rootroot00000000000000libqb-0.16.0/include/qb/Makefile.am000066400000000000000000000017421217426516200167620ustar00rootroot00000000000000# Copyright (c) 2010 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . # 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 libqb-0.16.0/include/qb/qbarray.h000066400000000000000000000062411217426516200165370ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_ARRAY_H_DEFINED #define QB_ARRAY_H_DEFINED #include #ifndef S_SPLINT_S #include #endif /* S_SPLINT_S */ #include /* *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 */ libqb-0.16.0/include/qb/qbatomic.h000066400000000000000000000160161217426516200166760ustar00rootroot00000000000000/* * 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 . */ /* * 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 #include #include /** * @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 */ libqb-0.16.0/include/qb/qbconfig.h.in000066400000000000000000000017771217426516200173040ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #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 */ libqb-0.16.0/include/qb/qbdefs.h000066400000000000000000000054441217426516200163460ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #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 * * 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 */ libqb-0.16.0/include/qb/qbhdb.h000066400000000000000000000110341217426516200161520ustar00rootroot00000000000000/* * Copyright (C) 2006-2010 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #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 #include #include #include /** * @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 */ libqb-0.16.0/include/qb/qbipc_common.h000066400000000000000000000032611217426516200175430ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_IPC_COMMON_H_DEFINED #define QB_IPC_COMMON_H_DEFINED #include /* *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 */ libqb-0.16.0/include/qb/qbipcc.h000066400000000000000000000164761217426516200163520ustar00rootroot00000000000000/* * Copyright (C) 2006-2007, 2009 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #ifndef QB_IPCC_H_DEFINED #define QB_IPCC_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include #include #include #include /** * @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); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_IPCC_H_DEFINED */ libqb-0.16.0/include/qb/qbipcs.h000066400000000000000000000265631217426516200163700ustar00rootroot00000000000000/* * Copyright (C) 2006-2009 Red Hat, Inc. * * Author: Steven Dake , * Angus Salkeld * * 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 . */ #ifndef QB_IPCS_H_DEFINED #define QB_IPCS_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include #include #include /** * @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 wether 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 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. */ typedef void (*qb_ipcs_connection_created_fn) (qb_ipcs_connection_t *c); /** * This is called after a connection has been disconnected. * * @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. */ 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. */ 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. */ 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); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* QB_IPCS_H_DEFINED */ libqb-0.16.0/include/qb/qblist.h000066400000000000000000000210371217426516200163740ustar00rootroot00000000000000/* * Copyright (C) 2006-2010, 2009 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #ifndef QB_LIST_H_DEFINED #define QB_LIST_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include /** * @file qblist.h * This is a kernel style list implementation. * * @author Steven Dake */ 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 */ libqb-0.16.0/include/qb/qblog.h000066400000000000000000000423051217426516200162030ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_LOG_H_DEFINED #define QB_LOG_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #include #include #include #include #include #include #include #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 Threaded logging. * To achieve non-blocking logging you can use threaded logging. 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, }; 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 */ libqb-0.16.0/include/qb/qbloop.h000066400000000000000000000167771217426516200164110ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_LOOP_H_DEFINED #define QB_LOOP_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include /** * @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 */ libqb-0.16.0/include/qb/qbmap.h000066400000000000000000000163651217426516200162060ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_MAP_H_DEFINED #define QB_MAP_H_DEFINED #include #ifndef S_SPLINT_S #include #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 Radix_tree * and Trie 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 */ libqb-0.16.0/include/qb/qbrb.h000066400000000000000000000206551217426516200160310ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_RB_H_DEFINED #define QB_RB_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include /** * @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 */ /** * 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 then this will always return non-null * but if it's type is QB_RB_NORMAL then when there is not enough space then * 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 */ libqb-0.16.0/include/qb/qbutil.h000066400000000000000000000163141217426516200164000ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_UTIL_H_DEFINED #define QB_UTIL_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include #ifndef S_SPLINT_S #include #endif /* S_SPLINT_S */ #include /** * @file qbutil.h * @author Angus Salkeld * * 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 */ libqb-0.16.0/include/tlist.h000066400000000000000000000130601217426516200156300ustar00rootroot00000000000000/* * Copyright (c) 2006-2007, 2009 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #ifndef QB_TLIST_H_DEFINED #define QB_TLIST_H_DEFINED #include "os_base.h" #include #include #include #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 */ libqb-0.16.0/lib/000077500000000000000000000000001217426516200134435ustar00rootroot00000000000000libqb-0.16.0/lib/.gitignore000066400000000000000000000000161217426516200154300ustar00rootroot00000000000000run_splint.sh libqb-0.16.0/lib/Makefile.am000066400000000000000000000045751217426516200155120ustar00rootroot00000000000000# # Copyright (C) 2010 Red Hat, Inc. # # Author: Angus Salkeld # # 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 . 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:16:0 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@ 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 libqb-0.16.0/lib/array.c000066400000000000000000000111671217426516200147330ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #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); } libqb-0.16.0/lib/atomic_int.h000066400000000000000000000067231217426516200157520ustar00rootroot00000000000000/* * Copyright (C) 2013 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #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 #include /* 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 */ libqb-0.16.0/lib/hashtable.c000066400000000000000000000277371217426516200155620ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #include "os_base.h" #include #include "util_int.h" #include "map_int.h" #include #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; } libqb-0.16.0/lib/hdb.c000066400000000000000000000151061217426516200143470ustar00rootroot00000000000000/* * Copyright (C) 2006-2010 Red Hat, Inc. * * Author: Steven Dake * * 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 . */ #include "os_base.h" #include #include 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); } libqb-0.16.0/lib/ipc_int.h000066400000000000000000000137331217426516200152500ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat, Inc. * * Author: Steven Dake * Angus Salkeld * * 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 . */ #ifndef QB_IPC_INT_H_DEFINED #define QB_IPC_INT_H_DEFINED #include "os_base.h" #include #include #include #include #include #include #include #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]; 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 */ libqb-0.16.0/lib/ipc_setup.c000066400000000000000000000372631217426516200156150ustar00rootroot00000000000000/* * Copyright (C) 2010,2013 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #if defined(HAVE_GETPEERUCRED) #include #endif #ifdef HAVE_SYS_UN_H #include #endif /* HAVE_SYS_UN_H */ #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include #include #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, UNIX_PATH_MAX, "%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; /* * 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, UNIX_PATH_MAX, "%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 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"); 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; 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, req->max_msg_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 = req->max_msg_size; c->response.max_msg_size = req->max_msg_size; c->event.max_msg_size = req->max_msg_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 { 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; } libqb-0.16.0/lib/ipc_shm.c000066400000000000000000000206231217426516200152340ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "ipc_int.h" #include "util_int.h" #include "ringbuffer_int.h" #include #include #include #include /* * utility functions * -------------------------------------------------------- */ /* * client functions * -------------------------------------------------------- */ static void qb_ipcc_shm_disconnect(struct qb_ipcc_connection *c) { 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; qb_ipcs_connection_unref(c); } } 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_ipcs_connection_ref(c); } else { qb_util_log(LOG_ERR, "Error adding socket to mainloop (%s).", c->description); goto cleanup_request_response; } r->hdr.error = 0; return 0; cleanup_request_response: qb_rb_close(c->request.u.shm.rb); cleanup_request: qb_rb_close(c->response.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; } libqb-0.16.0/lib/ipc_socket.c000066400000000000000000000434621217426516200157430ustar00rootroot00000000000000/* * Copyright (C) 2010,2013 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_SYS_UN_H #include #endif /* HAVE_SYS_UN_H */ #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include #include #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, UNIX_PATH_MAX, "%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); 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; if (dgram_verify_msg_size(max_msg_size) == 0) { return max_msg_size; } for (i = 1024; i < max_msg_size; i+=1024) { if (dgram_verify_msg_size(i) != 0) { break; } last = i; } 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); close(c->request.u.us.sock); close(c->event.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"); 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 && (time_waited < timeout || timeout == -1)) { result = qb_ipc_us_ready(one_way, NULL, time_to_wait, POLLIN); time_waited += time_to_wait; goto retry_peek; } else { return -errno; } } 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; } qb_ipcs_connection_ref(c); 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; } qb_ipcs_connection_ref(c); 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); qb_ipcs_connection_unref(c); (void)c->service->poll_fns.dispatch_del(c->setup.u.us.sock); qb_ipcs_connection_unref(c); } 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(); } libqb-0.16.0/lib/ipcc.c000066400000000000000000000214351217426516200145320ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "ipc_int.h" #include "util_int.h" #include #include 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; } 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; if (c == NULL) { return -EINVAL; } res = c->funcs.recv(&c->response, msg_ptr, msg_len, ms_timeout); return _check_connection_state_with(c, res, _response_sock_one_way_get(c), ms_timeout, POLLIN); } 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); qb_ipcc_us_sock_close(ow->u.us.sock); 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; } libqb-0.16.0/lib/ipcs.c000066400000000000000000000505321217426516200145520ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "util_int.h" #include "ipc_int.h" #include #include #include 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++; /* 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; qb_ipcs_connection_ref(c); 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: qb_ipcs_connection_unref(c); 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; } } libqb-0.16.0/lib/libqb.pc.in000066400000000000000000000003161217426516200154650ustar00rootroot00000000000000prefix=@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} libqb-0.16.0/lib/log.c000066400000000000000000000553371217426516200144050ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #ifdef HAVE_LINK_H #include #endif /* HAVE_LINK_H */ #include #include #ifdef HAVE_DLFCN_H #include #endif /* HAVE_DLFCN_H */ #include #include #include #include #include #include #include "log_int.h" #include "util_int.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, 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, 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 int32_t _cs_matches_filter_(struct qb_log_callsite *cs, enum qb_log_filter_type type, const char *text, uint8_t high_priority, uint8_t low_priority) { int32_t match = QB_FALSE; if (cs->priority > low_priority || cs->priority < high_priority) { return QB_FALSE; } if (strcmp(text, "*") == 0) { return QB_TRUE; } if (type == QB_LOG_FILTER_FILE || type == QB_LOG_FILTER_FUNCTION) { char token[500]; const char *offset = NULL; const char *next = text; do { offset = next; next = strchrnul(offset, ','); snprintf(token, 499, "%.*s", (int)(next - offset), offset); if (type == QB_LOG_FILTER_FILE) { match = (strstr(cs->filename, token) != NULL); } else { match = (strstr(cs->function, token) != NULL); } if (!match && next[0] != 0) { next++; } } while (match == QB_FALSE && next != NULL && next[0] != 0); } else if (type == QB_LOG_FILTER_FORMAT) { if (strstr(cs->format, text)) { match = QB_TRUE; } } 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->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->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->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->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 *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) { free(flt); return -ENOMEM; } flt->high_priority = high_priority; flt->low_priority = low_priority; flt->new_value = t; qb_list_add_tail(&flt->list, list_head); } 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); free(flt->text); free(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); free(flt->text); free(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, 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, 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, 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, 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 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 || c > QB_LOG_TAG_CLEAR_ALL) { return -EINVAL; } pthread_rwlock_rdlock(&_listlock); rc = _log_filter_store(t, c, type, text, high_priority, low_priority); if (rc < 0) { pthread_rwlock_unlock(&_listlock); return rc; } qb_list_for_each_entry(sect, &callsite_sections, list) { _log_filter_apply(sect, t, c, type, text, 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); free(flt->text); free(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); free(flt->text); free(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; } libqb-0.16.0/lib/log_blackbox.c000066400000000000000000000151301217426516200162350ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #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); } /* file lineno * tags * priority * function name length * function name * buffer length * 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); /* 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-blackbox", t->name); 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); } libqb-0.16.0/lib/log_dcs.c000066400000000000000000000124401217426516200152220ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #ifdef HAVE_LINK_H #include #endif #include #include #include #include #include #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); } libqb-0.16.0/lib/log_file.c000066400000000000000000000046171217426516200153770ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #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); } libqb-0.16.0/lib/log_format.c000066400000000000000000000451601217426516200157460ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #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; } libqb-0.16.0/lib/log_int.h000066400000000000000000000056001217426516200152500ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #ifndef _QB_LOG_INT_H_ #define _QB_LOG_INT_H_ #include #include #include 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; }; 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_ */ libqb-0.16.0/lib/log_syslog.c000066400000000000000000000036261217426516200157770ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_SYSLOG_H #include #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; } libqb-0.16.0/lib/log_thread.c000066400000000000000000000146121217426516200157230ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include #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); } libqb-0.16.0/lib/loop.c000066400000000000000000000111431217426516200145600ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #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) { 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); } libqb-0.16.0/lib/loop_int.h000066400000000000000000000051201217426516200154350ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_LOOP_INT_DEFINED #define QB_LOOP_INT_DEFINED #include 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 */ libqb-0.16.0/lib/loop_job.c000066400000000000000000000074541217426516200154240ustar00rootroot00000000000000/* * Copyright (C) 2006-2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #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; } libqb-0.16.0/lib/loop_poll.c000066400000000000000000000415141217426516200156130ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_SYS_RESOURCE_H #include #endif #include #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 < 30; 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; } libqb-0.16.0/lib/loop_poll_epoll.c000066400000000000000000000113141217426516200170010ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "loop_poll_int.h" #ifdef HAVE_SYS_EPOLL_H #include #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; } if (events[i].events == pe->ufd.revents || pe->state == QB_POLL_ENTRY_JOBLIST) { /* * entry already in the job queue. */ continue; } pe->ufd.revents = _epoll_to_poll_event_(events[i].events); 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; } libqb-0.16.0/lib/loop_poll_int.h000066400000000000000000000047041217426516200164720ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_LOOP_POLL_INT_DEFINED #define QB_LOOP_POLL_INT_DEFINED #include "os_base.h" #ifdef HAVE_SYS_POLL_H #include #endif /* HAVE_SYS_POLL_H */ #include #include #include #include #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 */ libqb-0.16.0/lib/loop_poll_kqueue.c000066400000000000000000000117401217426516200171700ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "loop_poll_int.h" #ifdef HAVE_SYS_EVENT_H #include #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; } libqb-0.16.0/lib/loop_poll_poll.c000066400000000000000000000050131217426516200166330ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #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; } libqb-0.16.0/lib/loop_timerlist.c000066400000000000000000000151741217426516200166640ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include #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); } libqb-0.16.0/lib/map.c000066400000000000000000000053161217426516200143710ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #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); } libqb-0.16.0/lib/map_int.h000066400000000000000000000044101217426516200152420ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef _QB_MAP_INT_H_ #define _QB_MAP_INT_H_ #include 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_ */ libqb-0.16.0/lib/ringbuffer.c000066400000000000000000000543571217426516200157560ustar00rootroot00000000000000/* * Copyright (C) 2010-2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "ringbuffer_int.h" #include #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 void _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:%zd; real_size:%zd; 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)) { _rb_chunk_reclaim(rb); } } 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 void _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; 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; } 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) { int 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); } 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)); rb = qb_rb_open("create_from_file", n_required, 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; } libqb-0.16.0/lib/ringbuffer_helper.c000066400000000000000000000162241217426516200173040ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "ringbuffer_int.h" #include 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; } libqb-0.16.0/lib/ringbuffer_int.h000066400000000000000000000053001217426516200166150ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef _RINGBUFFER_H_ #define _RINGBUFFER_H_ #include "os_base.h" #ifdef HAVE_SYS_MMAN_H #include #endif /* HAVE_SYS_MMAN_H */ #ifdef HAVE_SYS_SEM_H #include #endif #ifdef HAVE_SYS_IPC_H #include #endif #include "rpl_sem.h" #include "util_int.h" #include #include 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_ */ libqb-0.16.0/lib/rpl_sem.c000066400000000000000000000102501217426516200152460ustar00rootroot00000000000000/* * 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 * 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 #include "rpl_sem.h" #include #include 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; } libqb-0.16.0/lib/rpl_sem.h000066400000000000000000000032321217426516200152550ustar00rootroot00000000000000/* * 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 * 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 #include #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 */ libqb-0.16.0/lib/skiplist.c000066400000000000000000000320121217426516200154470ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include #include #include #include #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; } libqb-0.16.0/lib/strchrnul.c000066400000000000000000000017201217426516200156330ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #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; } libqb-0.16.0/lib/strlcat.c000066400000000000000000000030641217426516200152660ustar00rootroot00000000000000/* * Copyright (C) 2007 Alan Robertson * 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; } libqb-0.16.0/lib/strlcpy.c000066400000000000000000000025161217426516200153130ustar00rootroot00000000000000/* * Copyright (C) 2007 Alan Robertson * 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 /* * 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; } libqb-0.16.0/lib/trie.c000066400000000000000000000420411217426516200145530ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include #include #include #include #include #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); /* * 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; 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 */ qb_list_replace(&cur_node->notifier_head, &split_node->notifier_head); 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) { free(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; free(node->segment); free(node->children); free(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 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->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_map_notifier *tn; do { qb_list_for_each_safe(list, next, &c->notifier_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 qb_map_notifier *f; 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) { f = qb_list_entry(list, struct qb_map_notifier, list); trie_notify_ref(f); 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; } } trie_notify_deref(f); } 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; } libqb-0.16.0/lib/unix.c000066400000000000000000000244051217426516200145770ustar00rootroot00000000000000/* * Copyright (C) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #ifdef HAVE_SYS_SHM_H #include #endif #ifdef HAVE_SYS_MMAN_H #include #endif #include "util_int.h" #include #include #include #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 */ libqb-0.16.0/lib/util.c000066400000000000000000000175411217426516200145740ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include "util_int.h" #include #include #include #include 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; } libqb-0.16.0/lib/util_int.h000066400000000000000000000064571217426516200154570ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #ifndef QB_UTIL_INT_H_DEFINED #define QB_UTIL_INT_H_DEFINED #include "os_base.h" #include #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 5 #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 */ libqb-0.16.0/libqb.spec.in000066400000000000000000000036661217426516200152620ustar00rootroot00000000000000%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: http://www.libqb.org 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 - @version@-1-@numcomm@.@alphatag@.@dirty@ - Autotools generated version libqb-0.16.0/tests/000077500000000000000000000000001217426516200140375ustar00rootroot00000000000000libqb-0.16.0/tests/.gitignore000066400000000000000000000002151217426516200160250ustar00rootroot00000000000000*.test *.fdata bench-log bmc bmcpt bms loop rbreader rbwriter libqb auto_* format_compare_speed crash_test_dummy file_change_bytes test.conf libqb-0.16.0/tests/Makefile.am000066400000000000000000000127311217426516200160770ustar00rootroot00000000000000# Copyright (c) 2010 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . # 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 " > $@_ ;\ 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 libqb-0.16.0/tests/bench-log.c000066400000000000000000000051731217426516200160470ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake * * 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 . */ #include "os_base.h" #include #include #include #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; } libqb-0.16.0/tests/blackbox-segfault.sh000077500000000000000000000010651217426516200177750ustar00rootroot00000000000000#!/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 libqb-0.16.0/tests/bmc.c000066400000000000000000000114321217426516200147450ustar00rootroot00000000000000/* * Copyright (c) 2009 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@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 . */ #include "os_base.h" #include #include #include #include #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 \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; } libqb-0.16.0/tests/bmcpt.c000066400000000000000000000103401217426516200153060ustar00rootroot00000000000000/* * Copyright (c) 2009 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } libqb-0.16.0/tests/bms.c000066400000000000000000000220071217426516200147650ustar00rootroot00000000000000/* * Copyright (c) 2006-2009 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake * * 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 . */ #include "os_base.h" #include #include #include #include #include #include #ifdef HAVE_GLIB #include #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 \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; } libqb-0.16.0/tests/check_array.c000066400000000000000000000100241217426516200164530ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include 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; } libqb-0.16.0/tests/check_ipc.c000066400000000000000000000743541217426516200161300ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include #include #include #include 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 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; 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_MSG_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++; /* 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++; } stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE); ck_assert_int_eq(stats->event_q_length - num, num_bulk_events); free(stats); } 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) { 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); } } 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, }; 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); 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 int32_t stop_process(pid_t pid) { /* wait a bit for the server to shutdown by it's self */ usleep(100000); kill(pid, SIGTERM); waitpid(pid, NULL, 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; 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_MSG_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 int32_t recv_timeout = -1; static void test_ipc_txrx(void) { int32_t j; int32_t c = 0; size_t size; pid_t pid; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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_MSG_SIZE) break; if (send_and_check(IPC_MSG_REQ_TX_RX, size, recv_timeout, QB_TRUE) < 0) { break; } } if (turn_on_fc) { ck_assert_int_eq(fc_enabled, QB_TRUE); } qb_ipcc_disconnect(conn); stop_process(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; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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)); /* kill the server */ stop_process(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_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; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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_MSG_SIZE) break; if (send_and_check(IPC_MSG_REQ_DISPATCH, size, recv_timeout, QB_TRUE) < 0) { break; } } qb_ipcc_disconnect(conn); stop_process(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)); } 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); } } } 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) { 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; qb_loop_t *cl; int32_t fd; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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); 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; 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); stop_process(pid); } static void test_ipc_stress_test(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; qb_loop_t *cl; int32_t fd; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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); 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); 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; 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); stop_process(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; 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_MSG_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); qb_ipcc_disconnect(conn); stop_process(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; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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); stop_process(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) { struct qb_ipc_request_header req_header; struct qb_ipc_response_header res_header; struct iovec iov[1]; int32_t res; int32_t j; int32_t c = 0; pid_t pid; pid = run_function_in_new_process(run_ipc_server); fail_if(pid == -1); sleep(1); do { conn = qb_ipcc_connect(ipc_name, MAX_MSG_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); /* * 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); } ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn)); qb_ipcc_disconnect(conn); stop_process(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; 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_MSG_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); qb_ipcc_disconnect(conn); stop_process(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_server_fail_shm"); tcase_add_test(tc, test_ipc_server_fail_shm); tcase_set_timeout(tc, 6); 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, 6); 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, 6); suite_add_tcase(s, tc); tc = tcase_create("ipc_fc_shm"); tcase_add_test(tc, test_ipc_fc_shm); tcase_set_timeout(tc, 6); 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, 3); suite_add_tcase(s, tc); tc = tcase_create("ipc_event_on_created_shm"); tcase_add_test(tc, test_ipc_event_on_created_shm); 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_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, 6); 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, 6); 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, 6); suite_add_tcase(s, tc); tc = tcase_create("ipc_fc_us"); tcase_add_test(tc, test_ipc_fc_us); tcase_set_timeout(tc, 6); suite_add_tcase(s, tc); tc = tcase_create("ipc_exit_us"); tcase_add_test(tc, test_ipc_exit_us); tcase_set_timeout(tc, 6); 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); suite_add_tcase(s, tc); tc = tcase_create("ipc_disconnect_after_created_us"); tcase_add_test(tc, test_ipc_disconnect_after_created_us); 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; } libqb-0.16.0/tests/check_log.c000066400000000000000000000476401217426516200161340ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include 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); 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 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, __FILE__, 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; } libqb-0.16.0/tests/check_loop.c000066400000000000000000000455151217426516200163230ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include 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; } libqb-0.16.0/tests/check_map.c000066400000000000000000000556231217426516200161300ustar00rootroot00000000000000/* * Copyright (c) 2011 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include 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; } libqb-0.16.0/tests/check_rb.c000066400000000000000000000124371217426516200157520ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include #include #include #include #include #include #include #include #include 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); avail = qb_rb_space_free(rb); actual = qb_rb_chunk_write(rb, &hdr, 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, &hdr, 512, 0); if (actual < 0) { ck_assert_int_eq(0, qb_rb_chunks_used(rb)); break; } 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; } libqb-0.16.0/tests/check_util.c000066400000000000000000000111401217426516200163120ustar00rootroot00000000000000/* * Copyright (c) 2010 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake * * 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 . */ #include "os_base.h" #include #include #include #include #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; } libqb-0.16.0/tests/crash_test_dummy.c000066400000000000000000000047641217426516200175700ustar00rootroot00000000000000/* * Copyright (c) 2013 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include 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; } libqb-0.16.0/tests/file_change_bytes.c000066400000000000000000000060211217426516200176340ustar00rootroot00000000000000/* * Copyright (C) 2013 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse * * 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 . */ #include #include #include #include #include #include #include #include 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; char *input_file_name; 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); } libqb-0.16.0/tests/format_compare_speed.c000066400000000000000000000051031217426516200203600ustar00rootroot00000000000000/* * Copyright (c) 2013 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include 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; } libqb-0.16.0/tests/loop.c000066400000000000000000000103041217426516200151520ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include "os_base.h" #include #include #include #include #include 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; } libqb-0.16.0/tests/make-log-test.sh000077500000000000000000000010071217426516200170450ustar00rootroot00000000000000#!/usr/bin/env bash line= count=0 total=50000 echo "#include " echo "#include " 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 "}" libqb-0.16.0/tests/rbreader.c000066400000000000000000000036721217426516200160010ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat, Inc. * * Author: Angus Salkeld * * 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 . */ #include #include #include #include #include #include #include #include #include #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; } libqb-0.16.0/tests/rbwriter.c000066400000000000000000000067371217426516200160600ustar00rootroot00000000000000/* * Copyright (c) 2009 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 \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; } libqb-0.16.0/tests/resources.test000077500000000000000000000004641217426516200167610ustar00rootroot00000000000000#!/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 libqb-0.16.0/tests/test.conf.in000066400000000000000000000001351217426516200162710ustar00rootroot00000000000000if [ -z "@HAVE_SLOW_TESTS@" ] then export NUM_BB_TESTS=32 else export NUM_BB_TESTS=1024 fi libqb-0.16.0/tools/000077500000000000000000000000001217426516200140355ustar00rootroot00000000000000libqb-0.16.0/tools/.gitignore000066400000000000000000000000141217426516200160200ustar00rootroot00000000000000qb-blackbox libqb-0.16.0/tools/Makefile.am000066400000000000000000000020201217426516200160630ustar00rootroot00000000000000# Copyright (c) 2011 Red Hat, Inc. # # Authors: Angus Salkeld # # 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 . # 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 libqb-0.16.0/tools/qb_blackbox.c000066400000000000000000000022341217426516200164510ustar00rootroot00000000000000/* * Copyright (C) 2012 Andrew Beekhof * * 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 . */ #include 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; }