tizen 2.3.1 release tizen_2.3.1 submit/tizen_2.3.1/20150915.073838 tizen_2.3.1_release
authorjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 12:48:13 +0000 (21:48 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 12:48:13 +0000 (21:48 +0900)
114 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
Doxyfile [new file with mode: 0644]
LICENSE.LGPL2.1 [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
check_db_clean [new file with mode: 0755]
configure.ac [new file with mode: 0644]
data/buxton.conf [new file with mode: 0644]
data/buxton.service.in [new file with mode: 0644]
data/buxton.socket.in [new file with mode: 0644]
data/libbuxton.pc.in [new file with mode: 0644]
demo/gtk_client.c [new file with mode: 0644]
demo/gtk_client.h [new file with mode: 0644]
demo/hellocreategroup.c [new file with mode: 0644]
demo/helloget.c [new file with mode: 0644]
demo/hellonotify.c [new file with mode: 0644]
demo/helloremovegroup.c [new file with mode: 0644]
demo/helloset.c [new file with mode: 0644]
demo/hellosetlabel.c [new file with mode: 0644]
demo/hellounset.c [new file with mode: 0644]
demo/notifytest.c [new file with mode: 0644]
demo/timing.c [new file with mode: 0644]
docs/LICENSE.MIT [new file with mode: 0644]
docs/buxton-api.7 [new file with mode: 0644]
docs/buxton-protocol.7 [new file with mode: 0644]
docs/buxton-security.7 [new file with mode: 0644]
docs/buxton.7 [new file with mode: 0644]
docs/buxton.conf.5 [new file with mode: 0644]
docs/buxton_client_handle_response.3 [new file with mode: 0644]
docs/buxton_close.3 [new file with mode: 0644]
docs/buxton_create_group.3 [new file with mode: 0644]
docs/buxton_get_value.3 [new file with mode: 0644]
docs/buxton_key_create.3 [new file with mode: 0644]
docs/buxton_key_free.3 [new file with mode: 0644]
docs/buxton_key_get_group.3 [new file with mode: 0644]
docs/buxton_key_get_layer.3 [new file with mode: 0644]
docs/buxton_key_get_name.3 [new file with mode: 0644]
docs/buxton_key_get_type.3 [new file with mode: 0644]
docs/buxton_open.3 [new file with mode: 0644]
docs/buxton_register_notification.3 [new file with mode: 0644]
docs/buxton_remove_group.3 [new file with mode: 0644]
docs/buxton_response_key.3 [new file with mode: 0644]
docs/buxton_response_status.3 [new file with mode: 0644]
docs/buxton_response_type.3 [new file with mode: 0644]
docs/buxton_response_value.3 [new file with mode: 0644]
docs/buxton_set_conf_file.3 [new file with mode: 0644]
docs/buxton_set_label.3 [new file with mode: 0644]
docs/buxton_set_value.3 [new file with mode: 0644]
docs/buxton_unregister_notification.3 [new file with mode: 0644]
docs/buxton_unset_value.3 [new file with mode: 0644]
docs/buxtonctl.1 [new file with mode: 0644]
docs/buxtond.8 [new file with mode: 0644]
m4/.empty [new file with mode: 0644]
packaging/buxton.manifest [new file with mode: 0644]
packaging/buxton.spec [new file with mode: 0644]
src/cli/client.c [new file with mode: 0644]
src/cli/client.h [new file with mode: 0644]
src/cli/main.c [new file with mode: 0644]
src/core/daemon.c [new file with mode: 0644]
src/core/daemon.h [new file with mode: 0644]
src/core/main.c [new file with mode: 0644]
src/db/gdbm.c [new file with mode: 0644]
src/db/memory.c [new file with mode: 0644]
src/include/buxton.h [new file with mode: 0644]
src/libbuxton/lbuxton.c [new file with mode: 0644]
src/libbuxton/lbuxton.sym [new file with mode: 0644]
src/security/smack.c [new file with mode: 0644]
src/security/smack.h [new file with mode: 0644]
src/shared/backend.c [new file with mode: 0644]
src/shared/backend.h [new file with mode: 0644]
src/shared/buxtonarray.c [new file with mode: 0644]
src/shared/buxtonarray.h [new file with mode: 0644]
src/shared/buxtonclient.h [new file with mode: 0644]
src/shared/buxtondata.h [new file with mode: 0644]
src/shared/buxtonkey.h [new file with mode: 0644]
src/shared/buxtonlist.c [new file with mode: 0644]
src/shared/buxtonlist.h [new file with mode: 0644]
src/shared/buxtonresponse.h [new file with mode: 0644]
src/shared/buxtonstring.h [new file with mode: 0644]
src/shared/configurator.c [new file with mode: 0644]
src/shared/configurator.h [new file with mode: 0644]
src/shared/dictionary.c [new file with mode: 0644]
src/shared/dictionary.h [new file with mode: 0644]
src/shared/direct.c [new file with mode: 0644]
src/shared/direct.h [new file with mode: 0644]
src/shared/hashmap.c [new file with mode: 0644]
src/shared/hashmap.h [new file with mode: 0644]
src/shared/iniparser.c [new file with mode: 0644]
src/shared/iniparser.h [new file with mode: 0644]
src/shared/list.h [new file with mode: 0644]
src/shared/log.c [new file with mode: 0644]
src/shared/log.h [new file with mode: 0644]
src/shared/macro.h [new file with mode: 0644]
src/shared/protocol.c [new file with mode: 0644]
src/shared/protocol.h [new file with mode: 0644]
src/shared/serialize.c [new file with mode: 0644]
src/shared/serialize.h [new file with mode: 0644]
src/shared/util.c [new file with mode: 0644]
src/shared/util.h [new file with mode: 0644]
test/check_buxton.c [new file with mode: 0644]
test/check_configurator.c [new file with mode: 0644]
test/check_daemon.c [new file with mode: 0644]
test/check_shared_lib.c [new file with mode: 0644]
test/check_smack.c [new file with mode: 0644]
test/check_utils.c [new file with mode: 0644]
test/check_utils.h [new file with mode: 0644]
test/test-configurator.conf [new file with mode: 0644]
test/test-fail.ini.in [new file with mode: 0644]
test/test-pass.ini.in [new file with mode: 0644]
test/test.conf.in [new file with mode: 0644]
test/test.load2 [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..1f009d3
--- /dev/null
@@ -0,0 +1,56 @@
+/Makefile
+*.log
+/*.tar.bz2
+/*.tar.gz
+/*.tar.xz
+*.la
+*.lo
+*.o
+*~
+Makefile.in
+aclocal.m4
+config.*
+configure
+autom4te.cache
+compile
+install-sh
+missing
+ltmain.sh
+depcomp
+stamp-*
+libtool
+test-driver
+.deps
+.libs
+m4/*.m4
+.dirstamp
+*.trs
+/cscope.*
+/tags
+buxtond
+buxtonctl
+check_buxton
+data/buxton.service
+data/buxton.socket
+data/*.pc
+check_shared_lib
+log-check-stderr-file
+test/*.ini
+src/shared/constants.c
+check_daemon
+check_buxtond
+check_smack
+bxt_timing
+bxt_gtk_client
+*.gcda
+*.gcno
+/coverage
+test/databases/*.db
+test/test.conf
+check_configurator
+GPATH
+GRTAGS
+GTAGS
+bxt_hello*
+buxton-*
+debug_check_daemon.txt
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..35243ec
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Auke Kok <auke-jan.h.kok@intel.com>
+Brad Peters <brad.t.peters@intel.com>
+Michael Ikey Doherty <michael.i.doherty@intel.com>
+Michael Leibowitz <michael.leibowitz@intel.com>
+Patrick McCarty <patrick.mccarty@linux.intel.com>
+Shane Bryan <shane.bryan@intel.com>
+William Douglas <william.douglas@intel.com>
diff --git a/Doxyfile b/Doxyfile
new file mode 100644 (file)
index 0000000..d2ed4d1
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1869 @@
+# Doxyfile 1.8.3.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = "Buxton"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = 1
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "Configuration system"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = "docs"
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = dictionary.* iniparser.* list.* hashmap.* macro.*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/LICENSE.LGPL2.1 b/LICENSE.LGPL2.1
new file mode 100644 (file)
index 0000000..4362b49
--- /dev/null
@@ -0,0 +1,502 @@
+                  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.
+\f
+  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.
+\f
+                  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..62cbccc
--- /dev/null
@@ -0,0 +1,478 @@
+# declare vars
+AM_CFLAGS = -std=gnu99 -fstack-protector -Wall -pedantic \
+        -Wstrict-prototypes -Wundef -fno-common \
+        -Werror-implicit-function-declaration \
+        -Wformat -Wformat-security -Werror=format-security \
+       -Wconversion -Wunreachable-code
+if DEBUG
+AM_CFLAGS += -ggdb3 -O0
+endif
+if COVERAGE
+AM_CFLAGS += --coverage
+endif
+
+EXTRA_CFLAGS = -fPIE
+
+AM_CPPFLAGS = \
+       -I $(top_srcdir)/src/include \
+       -I $(top_srcdir)/src/core \
+       -I $(top_srcdir)/src/security \
+       -I $(top_srcdir)/src/shared \
+       -D_MODULE_DIRECTORY=\"$(MODULEDIR)\" \
+       -D_DEFAULT_CONFIGURATION_FILE=\"$(CONFPATH)\" \
+       -D_DB_PATH=\"$(DB_PATH)\" \
+       -D_BUXTON_SOCKET=\"$(BUXTON_SOCKET)\" \
+       -D_SMACK_LOAD_FILE=\"$(SMACK_LOAD_FILE)\"
+
+AM_LDFLAGS = \
+       -rdynamic
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+AUTOMAKE_OPTIONS = color-tests parallel-tests
+SUBDIRS = .
+noinst_LTLIBRARIES =
+include_HEADERS =
+lib_LTLIBRARIES =
+pkglib_LTLIBRARIES =
+DISTCHECK_CONFIGURE_FLAGS =  \
+       --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) --enable-debug
+
+systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
+systemdsystemunit_DATA = data/buxton.service data/buxton.socket
+
+systemdsystemunit-install-hook:
+       mkdir -p $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants
+       ln -sf ../buxton.socket $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants/buxton.socket
+
+install-data-local:
+       $(MKDIR_P) $(DESTDIR)$(localstatedir)/lib/buxton
+
+install-data-hook: systemdsystemunit-install-hook
+
+systemdsystemunit-uninstall-hook:
+       rm -f $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants/buxton.socket
+
+uninstall-hook: systemdsystemunit-uninstall-hook
+
+distclean-local:
+       rm -f log-check-stderr-file
+       rm -f test/databases/*.db
+
+# set library version info
+LIBBUXTON_CURRENT=1
+LIBBUXTON_REVISION=0
+LIBBUXTON_AGE=0
+
+pkgconfiglibdir=$(libdir)/pkgconfig
+pkgconfiglib_DATA = \
+       data/libbuxton.pc
+
+if MANPAGE
+dist_man_MANS = \
+       docs/buxton.7 \
+       docs/buxtonctl.1 \
+       docs/buxton-api.7 \
+       docs/buxton.conf.5 \
+       docs/buxtond.8 \
+       docs/buxton-protocol.7 \
+       docs/buxton-security.7 \
+       docs/buxton_client_handle_response.3 \
+       docs/buxton_close.3 \
+       docs/buxton_create_group.3 \
+       docs/buxton_get_value.3 \
+       docs/buxton_key_create.3 \
+       docs/buxton_key_free.3 \
+       docs/buxton_key_get_group.3 \
+       docs/buxton_key_get_layer.3 \
+       docs/buxton_key_get_name.3 \
+       docs/buxton_key_get_type.3 \
+       docs/buxton_open.3 \
+       docs/buxton_register_notification.3 \
+       docs/buxton_remove_group.3 \
+       docs/buxton_response_key.3 \
+       docs/buxton_response_status.3 \
+       docs/buxton_response_type.3 \
+       docs/buxton_response_value.3 \
+       docs/buxton_set_conf_file.3 \
+       docs/buxton_set_label.3 \
+       docs/buxton_set_value.3 \
+       docs/buxton_unregister_notification.3 \
+       docs/buxton_unset_value.3
+endif
+
+TESTS = \
+       check_db_clean \
+       check_buxton \
+       check_shared_lib \
+       check_daemon \
+       check_smack \
+       check_configurator
+
+if COVERAGE
+coverage:
+       mkdir -p coverage
+       lcov --compat-libtool --directory . --capture --output-file coverage/report
+       genhtml -o coverage/ coverage/report
+endif
+
+# set flags
+
+EXTRA_DIST = \
+       Doxyfile \
+       LICENSE.LGPL2.1 \
+       check_db_clean \
+       data/libbuxton.pc.in \
+       docs/LICENSE.MIT \
+       src/libbuxton/lbuxton.sym \
+       test/test.load2 \
+       test/test.conf \
+       test/test-configurator.conf
+
+dist_sysconf_DATA = \
+       data/buxton.conf
+
+sbin_PROGRAMS = \
+       buxtond
+
+bin_PROGRAMS = \
+       buxtonctl
+
+buxtond_SOURCES = \
+       src/core/daemon.c \
+       src/core/daemon.h \
+       src/core/main.c
+
+buxtond_LDADD = \
+       $(SYSTEMD_LIBS) \
+       libbuxton-shared.la
+
+buxtond_CFLAGS = \
+       $(AM_CFLAGS) \
+       $(EXTRA_CFLAGS)
+
+buxtond_LDFLAGS = \
+       -pie
+
+buxtonctl_SOURCES = \
+       src/cli/main.c \
+       src/cli/client.c \
+       src/cli/client.h
+
+buxtonctl_CFLAGS = \
+       $(AM_CFLAGS) \
+       $(EXTRA_CFLAGS)
+
+buxtonctl_LDADD = \
+       libbuxton.la \
+       libbuxton-shared.la
+
+buxtonctl_LDFLAGS = \
+       -pie
+
+noinst_LTLIBRARIES += \
+       libbuxton-shared.la
+
+libbuxton_shared_la_SOURCES = \
+       src/security/smack.c \
+       src/security/smack.h \
+       src/shared/backend.c \
+       src/shared/backend.h \
+       src/shared/buxtonarray.c \
+       src/shared/buxtonarray.h \
+       src/shared/buxtonclient.h \
+       src/shared/buxtondata.h \
+       src/shared/buxtonkey.h \
+       src/shared/buxtonlist.c \
+       src/shared/buxtonlist.h \
+       src/shared/buxtonresponse.h \
+       src/shared/buxtonstring.h \
+       src/shared/configurator.c \
+       src/shared/configurator.h \
+       src/shared/direct.c \
+       src/shared/direct.h \
+       src/shared/hashmap.c \
+       src/shared/hashmap.h \
+       src/shared/list.h \
+       src/shared/log.c \
+       src/shared/log.h \
+       src/shared/macro.h \
+       src/shared/protocol.c \
+       src/shared/protocol.h \
+       src/shared/serialize.c \
+       src/shared/serialize.h \
+       src/shared/util.c \
+       src/shared/util.h \
+       ${NULL}
+
+if USE_LOCAL_INIPARSER
+libbuxton_shared_la_SOURCES += \
+       src/shared/dictionary.c \
+       src/shared/dictionary.h \
+       src/shared/iniparser.c \
+       src/shared/iniparser.h
+endif
+
+libbuxton_shared_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       -static
+
+include_HEADERS += \
+       src/include/buxton.h
+
+lib_LTLIBRARIES += \
+       libbuxton.la
+
+libbuxton_la_SOURCES = \
+       src/libbuxton/lbuxton.c
+
+libbuxton_la_CFLAGS = \
+       $(AM_CFLAGS) \
+       @INIPARSER_CFLAGS@ \
+       -fvisibility=hidden
+
+libbuxton_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       @INIPARSER_LIBS@ \
+       -version-info $(LIBBUXTON_CURRENT):$(LIBBUXTON_REVISION):$(LIBBUXTON_AGE) \
+       -Wl,--version-script=$(top_srcdir)/src/libbuxton/lbuxton.sym
+
+libbuxton_la_LIBADD = \
+       libbuxton-shared.la \
+       -ldl
+
+pkglib_LTLIBRARIES += \
+       gdbm.la \
+       memory.la
+
+gdbm_la_SOURCES =  \
+       src/db/gdbm.c
+
+gdbm_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       -fvisibility=hidden \
+       -module \
+       -avoid-version
+
+gdbm_la_LIBADD = \
+       -lgdbm
+
+memory_la_SOURCES = \
+       src/db/memory.c
+
+memory_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       -fvisibility=hidden \
+       -module \
+       -avoid-version
+
+check_PROGRAMS = \
+       check_buxton \
+       check_shared_lib \
+       check_buxtond \
+       check_daemon \
+       check_smack \
+       check_configurator
+
+check_buxton_SOURCES = \
+       test/check_utils.c \
+       test/check_utils.h \
+        test/check_buxton.c
+check_buxton_CFLAGS = \
+       $(AM_CFLAGS) \
+       @CHECK_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_buxton_LDADD = \
+       @CHECK_LIBS@ \
+       libbuxton.la \
+       libbuxton-shared.la
+
+check_shared_lib_SOURCES = \
+       test/check_utils.c \
+       test/check_utils.h \
+        test/check_shared_lib.c
+check_shared_lib_CFLAGS = \
+       $(AM_CFLAGS) \
+       @CHECK_CFLAGS@ \
+       @INIPARSER_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_shared_lib_LDADD = \
+       @CHECK_LIBS@ \
+       @INIPARSER_LIBS@ \
+       libbuxton.la \
+       libbuxton-shared.la
+
+check_buxtond_SOURCES = \
+       test/check_utils.c \
+       test/check_utils.h \
+       src/core/daemon.c \
+       src/core/daemon.h \
+       src/core/main.c
+check_buxtond_CFLAGS = \
+       @CHECK_CFLAGS@ \
+       $(AM_CFLAGS) \
+       @INIPARSER_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_buxtond_LDADD = \
+       @CHECK_LIBS@ \
+       $(SYSTEMD_LIBS) \
+       libbuxton.la \
+       libbuxton-shared.la
+
+check_daemon_SOURCES = \
+       test/check_utils.c \
+       test/check_utils.h \
+       src/core/daemon.c \
+       src/core/daemon.h \
+        test/check_daemon.c
+check_daemon_CFLAGS = \
+       $(AM_CFLAGS) \
+       @CHECK_CFLAGS@ \
+       @INIPARSER_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_daemon_LDADD = \
+       @CHECK_LIBS@ \
+       @INIPARSER_LIBS@ \
+       libbuxton.la \
+       libbuxton-shared.la
+
+check_smack_SOURCES = \
+       test/check_utils.c \
+       test/check_utils.h \
+       test/check_smack.c
+check_smack_CFLAGS = \
+       $(AM_CFLAGS) \
+       @CHECK_CFLAGS@ \
+       @INIPARSER_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_smack_LDADD = \
+       @CHECK_LIBS@ \
+       @INIPARSER_LIBS@ \
+       libbuxton.la \
+       libbuxton-shared.la
+
+check_configurator_SOURCES = \
+       src/shared/configurator.c \
+       src/shared/configurator.h \
+       test/check_configurator.c
+check_configurator_CFLAGS = \
+       $(AM_CFLAGS) \
+       @CHECK_CFLAGS@ \
+       @INIPARSER_CFLAGS@ \
+       -DMAKE_CHECK \
+       -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \
+       -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\"
+check_configurator_LDADD = \
+       @CHECK_LIBS@ \
+       @INIPARSER_LIBS@ \
+       libbuxton-shared.la
+
+check_DATA = \
+       test/test-pass.ini \
+       test/test-fail.ini \
+       test/test.conf \
+       test/test.load2 \
+       ${NULL}
+
+if BUILD_DEMOS
+bin_PROGRAMS += \
+       bxt_timing \
+       bxt_hello_get \
+       bxt_hello_set \
+       bxt_hello_set_label \
+       bxt_hello_create_group \
+       bxt_hello_remove_group \
+       bxt_hello_unset \
+       bxt_hello_notify \
+       bxt_hello_notify_multi
+
+# Timing test
+bxt_timing_SOURCES = \
+       demo/timing.c
+bxt_timing_LDADD = \
+       libbuxton.la \
+       libbuxton-shared.la \
+       -lrt -lm
+
+bxt_hello_get_SOURCES = \
+       demo/helloget.c
+bxt_hello_get_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_get_LDADD = \
+       libbuxton.la
+
+bxt_hello_set_SOURCES = \
+       demo/helloset.c
+bxt_hello_set_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_set_LDADD = \
+       libbuxton.la
+
+bxt_hello_set_label_SOURCES = \
+       demo/hellosetlabel.c
+bxt_hello_set_label_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_set_label_LDADD = \
+       libbuxton.la
+
+bxt_hello_create_group_SOURCES = \
+       demo/hellocreategroup.c
+bxt_hello_create_group_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_create_group_LDADD = \
+       libbuxton.la
+
+bxt_hello_remove_group_SOURCES = \
+       demo/helloremovegroup.c
+bxt_hello_remove_group_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_remove_group_LDADD = \
+       libbuxton.la
+
+bxt_hello_unset_SOURCES = \
+       demo/hellounset.c
+bxt_hello_unset_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_unset_LDADD = \
+       libbuxton.la
+
+bxt_hello_notify_SOURCES = \
+       demo/hellonotify.c
+bxt_hello_notify_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_notify_LDADD = \
+       libbuxton.la
+
+bxt_hello_notify_multi_SOURCES = \
+       demo/notifytest.c
+bxt_hello_notify_multi_CFLAGS = \
+       $(AM_CFLAGS)
+bxt_hello_notify_multi_LDADD = \
+       libbuxton.la
+
+if BUILD_GTK_DEMO
+bin_PROGRAMS += \
+       bxt_gtk_client
+# GTK3 client demo
+bxt_gtk_client_SOURCES = \
+       demo/gtk_client.c \
+       demo/gtk_client.h
+bxt_gtk_client_LDADD = \
+       $(GTK3_LIBS) \
+       libbuxton.la \
+       libbuxton-shared.la
+bxt_gtk_client_CFLAGS = \
+       $(GTK3_CFLAGS) \
+       $(AM_CFLAGS)
+endif
+
+endif
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..dd7dd2b
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+Buxton
+======
+
+Buxton is a security-enabled configuration management system. It features a
+layered approach to configuration storage, with each layer containing an
+arbitrary number of groups, each of which may contain key-value pairs.
+Mandatory Access Control (MAC) is implemented at the group level and at the
+key-value level.
+
+Buxton provides a C library (libbuxton) for client applications to use.
+Internally, buxton uses a daemon (buxtond) for processing client requests and
+enforcing MAC. Also, a CLI (buxtonctl) is provided for interactive use and for
+use in shell scripts.
+
+
+Build dependencies
+==================
+
+- attr, to provide a required header file, xattr.h
+
+- check, to build the unit tests
+
+- gdbm, for key-value pair storage
+
+- Linux kernel headers, for the inotify header
+
+- systemd, for autodetection of service file locations, for socket activation
+  of buxtond, and the initialization of the Smack virtual filesystem
+
+
+Additional runtime dependencies
+===============================
+
+- Smack-enabled kernel, for MAC support
+  * CONFIG_SECURITY_SMACK=y
+
+
+Note: if running a kernel without Smack enabled, buxton can still be used for
+configuration management, but MAC will not be enforced.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..d3559e9
--- /dev/null
+++ b/TODO
@@ -0,0 +1,93 @@
+TODO format:
+Description: (description of task)
+Difficulty: [Simple|Medium|Complex]
+Time to complete: (estimate in days)
+Target: Version to be released in
+Status: If multi part change, what has been done so far
+
+Description: Update logic for smack access checks
+Difficulty: Medium
+Time to complete: 6
+Target: v1
+Status: In progress. Refer to https://github.com/sofar/buxton/wiki/Smack-TODO
+        for details.
+
+Description: Test multi-part messaging in the client-side of the library,
+            mirroring changes made to buxtond's protocol handling
+Difficulty: Complex
+Time to complete: ??
+Target: ??
+Status:
+
+Description: add checks for data consistency, and determine if gdbm needs to be
+            "reorganised" to ensure minimal I/O bottlenecking
+Difficulty: Medium
+Time to complete: 4
+Target: ??
+Status:
+
+Description: Tests for nonblocking calls between client and server
+Difficulty: Simple
+Time to complete: 4
+Target: ??
+Status:
+
+Description: Fixup list keys
+Difficulty: Medium
+Time to complete: 5
+Target: ??
+Status:
+
+Description: Bulk send receive messages
+Difficulty: Complex
+Time to complete: ??
+Target: ??
+Status:
+
+Description: Complete code coverage (minus exceptional cases)
+Difficulty: Simple
+Time to complete: 10
+Target: after v1
+Status: 79.6% of lines covered
+
+Description: Use BuxtonArray for message deserialization
+Difficulty: Medium
+Time to complete: 5
+Target: v2
+Status:
+
+Description: Remove iniparser from buxton
+Difficulty: Simple
+Time to complete: Done
+Target: ??
+Status: Needs merged but Ubuntu doesn't include it yet so holding off for now
+
+Description: Tizen programs converted to use Buxton
+Difficulty: Complex
+Time to complete: 10
+Target: v1
+Status:
+
+Description: Allow notification registration for non existant keys
+Difficulty: Simple
+Time to complete: 3
+Target: ??
+Status:
+
+Description: Add client library support for list_keys
+Difficulty: Medium
+Time to complete: 3
+Target: ??
+Status:
+
+Description: Ensure all public apis give copies of data
+Difficulty: Simple
+Time to complete: 1
+Target: ??
+Status: All done aside from list_keys which needs to be updated for the new api
+
+Description: Add Wextra to compiler flags once iniparser is gone
+Difficulty: Simple
+Time to complete: 1
+Target: ??
+Status:
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..ebc0ee1
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -e
+
+autoreconf --force --install --symlink --warnings=all
+
+args="\
+--sysconfdir=/etc \
+--localstatedir=/var \
+--prefix=/usr \
+--enable-silent-rules"
+
+./configure CFLAGS='-g -O0' $args "$@"
+make clean
diff --git a/check_db_clean b/check_db_clean
new file mode 100755 (executable)
index 0000000..a814976
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+rm -f test/databases/*.db
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..decec58
--- /dev/null
@@ -0,0 +1,243 @@
+
+AC_PREREQ([2.68])
+AC_INIT([buxton],[2],[william.douglas@intel.com],[buxton],[https://github.com/sofar/buxton])
+AM_INIT_AUTOMAKE([foreign -Wall -Werror -Wno-portability silent-rules subdir-objects color-tests no-dist-gzip dist-xz])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_SRCDIR([src/core/main.c])
+AC_CONFIG_HEADERS([config.h])
+AC_PREFIX_DEFAULT(/usr/local)
+
+LT_PREREQ(2.2)
+LT_INIT([disable-static])
+
+# Checks for programs.
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AC_PROG_MKDIR_P
+
+# Checks for libraries.
+AC_CHECK_LIB([dl], [dlopen])
+AC_CHECK_LIB([gdbm], [gdbm_open])
+AC_CHECK_LIB([rt], [clock_gettime])
+AC_CHECK_LIB([m], [sqrt])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_INLINE
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UID_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_FORK
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_FUNC_STRTOD
+AC_CHECK_FUNCS([atexit])
+AC_CHECK_FUNCS([memmove])
+AC_CHECK_FUNCS([memset])
+AC_CHECK_FUNCS([socket])
+AC_CHECK_FUNCS([strchr])
+AC_CHECK_FUNCS([strdup])
+AC_CHECK_FUNCS([strndup])
+AC_CHECK_FUNCS([strtol])
+AC_CHECK_FUNCS([__secure_getenv secure_getenv])
+
+AC_MSG_CHECKING([[whether preprocessor supports #pragma once]])
+AC_PREPROC_IFELSE(
+        [AC_LANG_PROGRAM([[#pragma once]])],
+        [
+                AC_MSG_RESULT([yes])
+                AC_DEFINE([HAVE_PRAGMA_ONCE], [1], [Preprocessor support for #pragma once])
+        ],
+        [AC_MSG_RESULT([no])])
+
+AH_VERBATIM([_GNU_SOURCE],
+[/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+       #define _GNU_SOURCE
+#endif])
+
+AH_TOP([#ifndef CONFIG_H_INCLUDED
+       #define CONFIG_H_INCLUDED 1])
+AH_BOTTOM([#endif])
+
+# PkgConfig tests
+PKG_CHECK_MODULES([CHECK], [check])
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd-daemon])
+
+# Checks for header files.
+AC_FUNC_ALLOCA
+AC_HEADER_STDBOOL
+AC_CHECK_HEADERS([attr/xattr.h], [], [AC_MSG_ERROR([Unable to fin xattr headers])])
+AC_CHECK_HEADERS([fcntl.h])
+AC_CHECK_HEADERS([gdbm.h], [], [AC_MSG_ERROR([Unable to find gdbm headers])])
+AC_CHECK_HEADERS([inttypes.h])
+AC_CHECK_HEADERS([limits.h])
+AC_CHECK_HEADERS([locale.h])
+AC_CHECK_HEADERS([malloc.h])
+AC_CHECK_HEADERS([stddef.h])
+AC_CHECK_HEADERS([stdint.h])
+AC_CHECK_HEADERS([stdlib.h])
+AC_CHECK_HEADERS([string.h])
+AC_CHECK_HEADERS([time.h])
+AC_CHECK_HEADERS([math.h])
+AC_CHECK_HEADERS([pthread.h])
+AC_CHECK_HEADERS([sys/param.h])
+AC_CHECK_HEADERS([sys/signalfd.h])
+AC_CHECK_HEADERS([sys/socket.h])
+AC_CHECK_HEADERS([sys/stat.h])
+AC_CHECK_HEADERS([sys/time.h])
+AC_CHECK_HEADERS([unistd.h])
+AC_CHECK_HEADERS([linux/inotify.h])
+AC_CHECK_FUNC(inotify_init)
+
+# Options
+AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
+       [path to systemd system service directory]), [path_systemdsystemunit=${withval}],
+       [path_systemdsystemunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+SYSTEMD_SYSTEMUNITDIR="${path_systemdsystemunit}"
+AC_SUBST(SYSTEMD_SYSTEMUNITDIR)
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdsystemunit}")
+
+AC_ARG_WITH([user], AS_HELP_STRING([--with-user=USER],
+       [user to run buxton as]), [username=${withval}],
+       [username="buxton"])
+BUXTON_USERNAME="${username}"
+AC_SUBST(BUXTON_USERNAME)
+
+INIPARSER_CFLAGS="-I${srcdir}/src/shared/"
+AC_ARG_WITH([local-iniparser],
+       AS_HELP_STRING([--with-local-iniparser[default=yes]],
+               [Use built-in iniparser for config parsing]),
+       [use_local_iniparser="true"],
+       [PKG_CHECK_MODULES([INIPARSER], [iniparser >= 3.1],
+               [use_local_iniparser="false"],
+               [use_local_iniparser="true"; AC_SUBST(INIPARSER_CFLAGS)])])
+AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test x$use_local_iniparser = x"true"])
+
+AC_ARG_WITH([module-dir], AS_HELP_STRING([--with-module-dir=MODULEDIR],
+       [path to buxton modules]), [moduledir=${withval}],
+       [moduledir="${libdir}/buxton"])
+MODULEDIR="${moduledir}"
+AC_SUBST(MODULEDIR)
+
+AC_ARG_WITH([config-path], AS_HELP_STRING([--with-config-path=CONFPATH],
+       [path to buxton configuration file]), [confpath=${withval}],
+       [confpath="${sysconfdir}/buxton.conf"])
+CONFPATH="${confpath}"
+AC_SUBST(CONFPATH)
+
+AC_ARG_WITH([db-path], AS_HELP_STRING([--with-db-path=DBPATH],
+       [path to buxton db files]), [dbpath=${withval}],
+       [dbpath="${localstatedir}/lib/buxton"])
+DB_PATH="${dbpath}"
+AC_SUBST(DB_PATH)
+
+AC_ARG_WITH([socket-path], AS_HELP_STRING([--with-socket-path=SOCKETPATH],
+       [path to buxton socket file]), [socketpath=${withval}],
+       [socketpath="/run/buxton-0"])
+BUXTON_SOCKET="${socketpath}"
+AC_SUBST(BUXTON_SOCKET)
+
+AC_ARG_WITH([smack-load-file], AS_HELP_STRING([--with-smack-load-file=SMACKLOADFILE],
+       [path to smack load2 file]), [smack_load_file=${withval}],
+       [smack_load_file="/sys/fs/smackfs/load2"])
+SMACK_LOAD_FILE="${smack_load_file}"
+AC_SUBST(SMACK_LOAD_FILE)
+
+AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debug mode @<:@default=no@:>@]),
+             [], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"],
+       [AC_DEFINE([DEBUG], [1], [Debugging mode enabled])],
+       [AC_DEFINE([NDEBUG], [1], [Debugging and assertions disabled])])
+AM_CONDITIONAL([DEBUG], [test x$enable_debug = x"yes"])
+
+AC_ARG_ENABLE(manpages, AS_HELP_STRING([--enable-manpages], [enable man pages @<:@default=yes@:>@]),
+             [], [enable_manpages=yes])
+AS_IF([test "x$enable_manpages" = "xyes"],
+       [AC_DEFINE([MANPAGE], [1], [Man pages will be included])],
+       [AC_DEFINE([NMANPAGE], [1], [Man pages will not be included])])
+AM_CONDITIONAL([MANPAGE], [test x$enable_manpages = x"yes"])
+
+have_coverage=no
+AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage]))
+if test "x$enable_coverage" = "xyes" ; then
+        AC_CHECK_PROG(lcov_found, [lcov], [yes], [no])
+        if test "x$lcov_found" = xno ; then
+                AC_MSG_ERROR([*** lcov support requested but the program was not found])
+        else
+                lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`"
+                lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`"
+                if test "$lcov_version_major" -eq 1 -a "$lcov_version_minor" -lt 10; then
+                        AC_MSG_ERROR([*** lcov version is too old. 1.10 required])
+                else
+                       have_coverage=yes
+                       AC_DEFINE([COVERAGE], [1], [Coverage enabled])
+                fi
+        fi
+fi
+AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"])
+
+AC_ARG_ENABLE(demos, AS_HELP_STRING([--enable-demos], [enable demos @<:@default=no@:>@]),
+             [], [enable_demos=no])
+AS_IF([test "x$enable_demos" = "xyes"],
+       [AC_DEFINE([DEMOS], [1], [Building demos])],
+       [])
+AM_CONDITIONAL([BUILD_DEMOS], [test x$enable_demos = x"yes"])
+
+# GTK3 demo
+AC_ARG_ENABLE(gtk-demo, AS_HELP_STRING([--enable-gtk-demo], [enable demos @<:@default=no@:>@]),
+             [], [enable_gtk_demo=no])
+AS_IF([test "x$enable_gtk_demo" = "xyes"],
+       [AC_DEFINE([DEMOS], [1], [Building GTK demo])],
+       [])
+AM_CONDITIONAL([BUILD_GTK_DEMO], [test x$enable_gtk_demo = x"yes"])
+
+if test "x$enable_gtk_demo" = "xyes"; then
+       # Require GTK3 for client demonstration
+       PKG_CHECK_MODULES([GTK3], [gtk+-3.0 >= 3.10])
+fi
+
+AC_CONFIG_COMMANDS([mkdir], [$MKDIR_P test/databases])
+AC_CONFIG_FILES([
+data/buxton.service
+data/buxton.socket
+data/libbuxton.pc
+test/test-pass.ini
+test/test-fail.ini
+test/test.conf
+])
+AC_OUTPUT
+
+AC_MSG_RESULT([
+        buxton $VERSION
+        ========
+
+        prefix:                 ${prefix}
+        libdir:                 ${libdir}
+        sysconfdir:             ${sysconfdir}
+        exec_prefix:            ${exec_prefix}
+        bindir:                 ${bindir}
+        sbindir:                ${sbindir}
+        datarootdir:            ${datarootdir}
+        mandir:                 ${mandir}
+        modules:                ${MODULEDIR}
+
+        compiler:               ${CC}
+        cflags:                 ${CFLAGS}
+        ldflags:                ${LDFLAGS}
+
+        debug:                  ${enable_debug}
+        demos:                  ${enable_demos}
+        coverage:               ${have_coverage}
+        manpages:               ${enable_manpages}
+])
diff --git a/data/buxton.conf b/data/buxton.conf
new file mode 100644 (file)
index 0000000..e1aad8e
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Default buxton config file
+#
+
+[Configuration]
+#ModuleDirectory=${libdir}/buxton
+#DatabasePath=${localstatedir}/lib/buxton
+#SmackLoadFile=/sys/fs/smackfs/load2
+#SocketPath=/run/buxton-0
+
+[base]
+Type=System
+Backend=gdbm
+Description=Operating System configuration layer
+Priority=0
+# This will end up being a file at @@DB_PATH@@/base.db
+
+[ro-base]
+Type=System
+Backend=gdbm
+Description=Operating System configuration layer
+Priority=0
+#Access=read-only
+# This will end up being a file at @@DB_PATH@@/base.db
+
+[isp]
+Type=System
+Backend=gdbm
+Description=ISP specific settings
+Priority=1
+# This will end up being a file at @@DB_PATH@@/isp.db
+
+[temp]
+Type=System
+Backend=memory
+Priority=99
+Description=A temporary layer for scratch settings and data
+# This will not end up in any file
+
+[user]
+Type=User
+Backend=gdbm
+Priority=1000
+Description=Per-user settings
+# This will end up in @@DB_PATH@@/user-<uid>.db
diff --git a/data/buxton.service.in b/data/buxton.service.in
new file mode 100644 (file)
index 0000000..d306864
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Buxton Configuration Service
+
+[Service]
+ExecStart=@prefix@/sbin/buxtond
+User=@BUXTON_USERNAME@
+
+[Install]
+WantedBy=multi-user.target
diff --git a/data/buxton.socket.in b/data/buxton.socket.in
new file mode 100644 (file)
index 0000000..bade73a
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Buxton Configuration Service
+
+[Socket]
+ListenStream=@BUXTON_SOCKET@
+
+[Install]
+WantedBy=sockets.target
diff --git a/data/libbuxton.pc.in b/data/libbuxton.pc.in
new file mode 100644 (file)
index 0000000..f731570
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: buxton
+Description: Library for buxton clients
+URL: @PACKAGE_URL@
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lbuxton
+Cflags: -I${includedir}
diff --git a/demo/gtk_client.c b/demo/gtk_client.c
new file mode 100644 (file)
index 0000000..8b8105f
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib-unix.h>
+
+#include "buxton.h"
+#include "gtk_client.h"
+
+/* BuxtonTest object */
+struct _BuxtonTest {
+        GtkWindow parent;
+        BuxtonClient client;
+        gint fd;
+        GtkWidget *info_label;
+        GtkWidget *info;
+        GtkWidget *value_label;
+        GtkWidget *entry;
+        guint tag;
+        gboolean setting;
+};
+
+/* BuxtonTest class definition */
+struct _BuxtonTestClass {
+        GtkWindowClass parent_class;
+};
+
+G_DEFINE_TYPE(BuxtonTest, buxton_test, GTK_TYPE_WINDOW)
+
+/* Boilerplate GObject code */
+static void buxton_test_class_init(BuxtonTestClass *klass);
+static void buxton_test_init(BuxtonTest *self);
+static void buxton_test_dispose(GObject *object);
+
+static void update_key(GtkWidget *self, gpointer userdata);
+static void update_value(BuxtonTest *self);
+static void report_error(BuxtonTest *self, gchar *error);
+static void buxton_callback(BuxtonResponse response, gpointer userdata);
+static gboolean buxton_update(gint fd, GIOCondition cond, gpointer userdata);
+
+/**
+ * Initialise Buxton
+ */
+static gboolean buxton_init(BuxtonTest *self);
+
+/* Initialisation */
+static void buxton_test_class_init(BuxtonTestClass *klass)
+{
+       GObjectClass *g_object_class;
+
+       g_object_class = G_OBJECT_CLASS(klass);
+       g_object_class->dispose = &buxton_test_dispose;
+}
+
+static void buxton_test_init(BuxtonTest *self)
+{
+       GtkWidget *info, *layout;
+       GtkWidget *label, *container, *box, *box2;
+       GtkWidget *entry, *button;
+       GtkStyleContext *style;
+
+       /* Window setup */
+       g_signal_connect(self, "destroy", gtk_main_quit, NULL);
+       gtk_window_set_default_size(GTK_WINDOW(self), 700, 300);
+       gtk_window_set_title(GTK_WINDOW(self), "BuxtonTest");
+
+       /* layout */
+       layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+       gtk_container_add(GTK_CONTAINER(self), layout);
+
+       info = gtk_info_bar_new();
+       label = gtk_label_new("Connecting");
+       self->info_label = label;
+       self->info = info;
+       container = gtk_info_bar_get_content_area(GTK_INFO_BAR(info));
+       gtk_container_add(GTK_CONTAINER(container), label);
+       gtk_box_pack_start(GTK_BOX(layout), info, FALSE, FALSE, 0);
+
+       /* Help label */
+       label = gtk_label_new("<big>"
+               "Using the controls below, you can set a key within the\n"
+               "<b>user</b> layer. Open another instance of this client to\n"
+               "check notification support.</big>");
+       gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+       gtk_box_pack_start(GTK_BOX(layout), label, FALSE, FALSE, 10);
+
+       box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+       gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
+       gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
+       gtk_box_pack_start(GTK_BOX(layout), box, TRUE, TRUE, 0);
+
+       /* Updated to key value */
+       label = gtk_label_new("<big>\'test\' value:</big>");
+       self->value_label = label;
+       gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+       gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 10);
+
+       box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+       style = gtk_widget_get_style_context(box2);
+       gtk_style_context_add_class(style, GTK_STYLE_CLASS_LINKED);
+       gtk_box_pack_start(GTK_BOX(box), box2, TRUE, TRUE, 0);
+
+       /* Give entry and button a linked effect */
+       entry = gtk_entry_new();
+       self->entry = entry;
+       gtk_entry_set_placeholder_text(GTK_ENTRY(self->entry),
+               "Type a new value");
+       g_signal_connect(entry, "activate", G_CALLBACK(update_key), self);
+       gtk_box_pack_start(GTK_BOX(box2), entry, TRUE, TRUE, 0);
+
+       button = gtk_button_new_with_label("Update");
+       g_signal_connect(button, "clicked", G_CALLBACK(update_key), self);
+       gtk_box_pack_start(GTK_BOX(box2), button, FALSE, FALSE, 0);
+
+       gtk_widget_show_all(GTK_WIDGET(self));
+       gtk_widget_grab_focus(button);
+
+       self->fd = -1;
+        self->setting = FALSE;
+        gtk_widget_hide(info);
+
+       /* Attempt connection to Buxton */
+       if (!buxton_init(self)) {
+               gtk_info_bar_set_message_type(GTK_INFO_BAR(info),
+                       GTK_MESSAGE_ERROR);
+               gtk_label_set_markup(GTK_LABEL(self->info_label), "No connection!");
+               gtk_widget_show(info);
+       } else {
+               update_value(self);
+       }
+}
+
+static void buxton_test_dispose(GObject *object)
+{
+       BuxtonTest *self = BUXTON_TEST(object);
+       if (self->tag > 0) {
+               g_source_remove(self->tag);
+               self->tag = 0;
+       }
+       if (self->client) {
+               buxton_close(self->client);
+               self->client = NULL;
+       }
+        /* Destruct */
+        G_OBJECT_CLASS (buxton_test_parent_class)->dispose (object);
+}
+
+/* Utility; return a new BuxtonTest */
+GtkWidget* buxton_test_new(void)
+{
+       BuxtonTest *self;
+
+       self = g_object_new(BUXTON_TEST_TYPE, NULL);
+       return GTK_WIDGET(self);
+}
+static gboolean buxton_init(BuxtonTest *self)
+{
+       gint fd;
+       BuxtonKey key;
+
+       /* Bail if initialized */
+       if (self->fd > 0) {
+               return TRUE;
+       }
+       /* Stop probing Buxton */
+       if (self->tag > 0) {
+               g_source_remove(self->tag);
+               self->tag = 0;
+       }
+
+       fd = buxton_open(&self->client);
+       if (fd <= 0) {
+               return FALSE;
+       }
+       self->fd = fd;
+
+       /* Poll Buxton events on idle loop, Buxton will then dispatch them
+        * to appropriate callbacks */
+       self->tag = g_unix_fd_add(self->fd, G_IO_IN | G_IO_PRI | G_IO_HUP,
+               buxton_update, self->client);
+
+       /* Register primary key */
+       key = buxton_key_create(GROUP, PRIMARY_KEY, LAYER, STRING);
+       if (buxton_register_notification(self->client, key,
+                                        buxton_callback, self, false)) {
+               report_error(self, "Unable to register for notifications");
+       }
+
+       return TRUE;
+}
+
+static void update_key(GtkWidget *widget, gpointer userdata)
+{
+       BuxtonTest *self = BUXTON_TEST(userdata);
+       BuxtonKey key;
+       const gchar *value;
+
+       value = gtk_entry_get_text(GTK_ENTRY(self->entry));
+       if (strlen(value) == 0 || g_str_equal(value, "")) {
+               return;
+       }
+
+       key = buxton_key_create(GROUP, PRIMARY_KEY, LAYER, STRING);
+
+        self->setting = TRUE;
+       if (buxton_set_value(self->client, key, (void*)value,
+                            buxton_callback, self, false)) {
+               report_error(self, "Unable to set value!");
+       }
+       buxton_key_free(key);
+}
+
+static void update_value(BuxtonTest *self)
+{
+       BuxtonKey key;
+
+       key = buxton_key_create(GROUP, PRIMARY_KEY, LAYER, STRING);
+
+       if (buxton_get_value(self->client, key,
+                            buxton_callback, self, false)) {
+               /* Buxton disconnects us when this happens. ##FIXME##
+                * We force a reconnect */
+               report_error(self, "Cannot retrieve value");
+               buxton_close(self->client);
+               self->fd = -1;
+               /* Just try reconnecting */
+               if (!buxton_init(self)) {
+                       report_error(self, "Unable to connect");
+               }
+       }
+
+
+       buxton_key_free(key);
+}
+
+static void report_error(BuxtonTest *self, gchar *error)
+{
+       if (error != NULL) {
+               printf("Error! %s\n", error);
+               gtk_label_set_markup(GTK_LABEL(self->info_label), error);
+               gtk_widget_show_all(GTK_WIDGET(self->info));
+               gtk_info_bar_set_message_type(GTK_INFO_BAR(self->info),
+                       GTK_MESSAGE_ERROR);
+       } else {
+               gtk_widget_hide(GTK_WIDGET(self->info));
+       }
+}
+
+static gboolean buxton_update(gint fd, GIOCondition cond, gpointer userdata)
+{
+       BuxtonClient client = (BuxtonClient)userdata;
+       ssize_t handled = buxton_client_handle_response(client);
+       return (handled >= 0);
+}
+
+static void buxton_callback(BuxtonResponse response, gpointer userdata)
+{
+       BuxtonKey key;
+       BuxtonTest *self;
+       void *value;
+       gchar *key_name = NULL;
+       self = BUXTON_TEST(userdata);
+
+        /* Handle all potential async cases we're utilizing */
+        if (buxton_response_status(response) != 0) {
+                switch (buxton_response_type(response)) {
+                        case BUXTON_CONTROL_GET:
+                                report_error(self, "Cannot retrieve value");
+                                return;
+                        case BUXTON_CONTROL_SET:
+                                self->setting = FALSE;
+                                report_error(self, "Unable to set value");
+                                return;
+                        case BUXTON_CONTROL_CHANGED:
+                                report_error(self, "Unable to get notification value");
+                                return;
+                        case BUXTON_CONTROL_NOTIFY:
+                                report_error(self, "Unable to register for notification");
+                                return;
+                        default:
+                                report_error(self, "Unhandled error!");
+                                return;
+                }
+        }
+        if (self->setting) {
+                self->setting = FALSE;
+                return;
+        }
+
+       key = buxton_response_key(response);
+       key_name = buxton_key_get_name(key);
+       value = buxton_response_value(response);
+
+       /* Handle PRIMARY_KEY (string) */
+       if (g_str_equal(key_name, PRIMARY_KEY) && buxton_key_get_type(key) == STRING) {
+               gchar *lab;
+               /* Key unset */
+               if (!value) {
+                       lab = g_strdup_printf("<big>\'%s\' unset</big>", key_name);
+               } else {
+                       lab = g_strdup_printf("<big>\'%s\' value: %s</big>",
+                               key_name, (gchar*)value);
+               }
+               /* Update UI */
+               gtk_label_set_markup(GTK_LABEL(self->value_label), lab);
+               g_free(lab);
+       }
+
+       free(value);
+       free(key_name);
+       buxton_key_free(key);
+}
+
+/** Main entry */
+int main(int argc, char **argv)
+{
+       __attribute__ ((unused)) GtkWidget *window = NULL;
+
+       gtk_init(&argc, &argv);
+       window = buxton_test_new();
+       gtk_main();
+
+       return EXIT_SUCCESS;
+}
diff --git a/demo/gtk_client.h b/demo/gtk_client.h
new file mode 100644 (file)
index 0000000..80b23ea
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+typedef struct _BuxtonTest BuxtonTest;
+typedef struct _BuxtonTestClass   BuxtonTestClass;
+
+#define BUXTON_TEST_TYPE (buxton_test_get_type())
+#define BUXTON_TEST(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUXTON_TEST_TYPE, BuxtonTest))
+#define IS_BUXTON_TEST(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUXTON_TEST_TYPE))
+#define BUXTON_TEST_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), BUXTON_TEST_TYPE, BuxtonTestClass))
+#define IS_BUXTON_TEST_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), BUXTON_TEST_TYPE))
+#define BUXTON_TEST_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), BUXTON_TEST_TYPE, BuxtonTestClass))
+
+#define PRIMARY_KEY "test"
+#define GROUP "test"
+#define LAYER "user"
+
+GType buxton_test_get_type(void);
+
+/* BuxtonTest methods */
+GtkWidget* buxton_test_new(void);
diff --git a/demo/hellocreategroup.c b/demo/hellocreategroup.c
new file mode 100644 (file)
index 0000000..0b1c3df
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void create_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to create group\n");
+       } else {
+               printf("Created group\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", NULL, "user", STRING);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_create_group(client, key, create_cb,
+                               NULL, false)) {
+               printf("create group call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/helloget.c b/demo/helloget.c
new file mode 100644 (file)
index 0000000..27cf132
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void get_cb(BuxtonResponse response, void *data)
+{
+       int32_t* ret = (int32_t*)data;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to get value\n");
+               return;
+       }
+
+       *ret = *(int32_t*)buxton_response_value(response);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int32_t gvalue = -1;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_get_value(client, key, get_cb,
+                            &gvalue, false)) {
+               printf("get call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       if (gvalue >= 0) {
+               printf("got value: %d\n", gvalue);
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/hellonotify.c b/demo/hellonotify.c
new file mode 100644 (file)
index 0000000..f191bf4
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "buxton.h"
+
+void notify_cb(BuxtonResponse response, void *data)
+{
+       bool *status = (bool *)data;
+       BuxtonKey key;
+       int32_t *value;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               *status = false;
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+
+       value = (int32_t*)buxton_response_value(response);
+       if (value) {
+               printf("key %s updated with new value %d\n", name, *value);
+       } else {
+               printf("key %s was removed\n", name);
+       }
+
+       buxton_key_free(key);
+       free(value);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       bool status = true;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       int repoll_count = 10;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", NULL, INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_register_notification(client, key, notify_cb, &status, false)) {
+               printf("set call failed to run\n");
+               return -1;
+       }
+repoll:
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r < 0) {
+               printf("poll error\n");
+               return -1;
+       } else if (r == 0) {
+               if (repoll_count-- > 0) {
+                       goto out;
+               }
+               goto repoll;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       if (!status) {
+               printf("Failed to register for notification\n");
+               return -1;
+       }
+
+       goto repoll;
+
+out:
+       if (buxton_unregister_notification(client, key, NULL, NULL, true)) {
+               printf("Unregistration of notification failed\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/helloremovegroup.c b/demo/helloremovegroup.c
new file mode 100644 (file)
index 0000000..d1dfc15
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void remove_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to remove group\n");
+       } else {
+               printf("Removed group\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", NULL, "user", STRING);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_remove_group(client, key, remove_cb,
+                               NULL, false)) {
+               printf("remove group call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/helloset.c b/demo/helloset.c
new file mode 100644 (file)
index 0000000..cb73103
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void set_cb(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to set value\n");
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+       printf("Set value for key %s\n", name);
+       buxton_key_free(key);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       int32_t set;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       set = 10;
+
+       if (buxton_set_value(client, key, &set, set_cb,
+                            NULL, false)) {
+               printf("set call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/hellosetlabel.c b/demo/hellosetlabel.c
new file mode 100644 (file)
index 0000000..5dc792b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void set_label_cb(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to set label\n");
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+       printf("Set label for key %s\n", name);
+       buxton_key_free(key);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       char* label = "label-test";
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_set_label(client, key, label, set_label_cb,
+                            NULL, false)) {
+               printf("set label call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/hellounset.c b/demo/hellounset.c
new file mode 100644 (file)
index 0000000..ebff2c4
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void unset_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to unset value\n");
+       } else {
+               printf("Unset value\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_unset_value(client, key, unset_cb,
+                              NULL, false)) {
+               printf("unset call failed to run\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/notifytest.c b/demo/notifytest.c
new file mode 100644 (file)
index 0000000..16cb63f
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "buxton.h"
+
+void set_cb(BuxtonResponse response, void *data)
+{
+       bool *status = (bool *)data;
+       if (buxton_response_status(response) != 0) {
+               *status = false;
+       } else {
+               *status = true;
+       }
+}
+
+void notify_cb(BuxtonResponse response, void *data)
+{
+       bool *status = (bool *)data;
+       BuxtonKey key;
+       int32_t *value;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               *status = false;
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+
+       value = (int32_t*)buxton_response_value(response);
+       if (value) {
+               printf("key %s updated with new value %d\n", name, *value);
+       } else {
+               printf("key %s was removed\n", name);
+       }
+
+       buxton_key_free(key);
+       free(value);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key1, key2, key3, key4;
+       bool status = true;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       int do_update = 0;
+       int32_t val = 10;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\n");
+               return -1;
+       }
+
+       key1 = buxton_key_create("hello", "test1", NULL, INT32);
+       if (!key1) {
+               return -1;
+       }
+
+       key2 = buxton_key_create("hello", "test2", NULL, INT32);
+       if (!key2) {
+               return -1;
+       }
+
+       key3 = buxton_key_create("hello", "test3", NULL, INT32);
+       if (!key3) {
+               return -1;
+       }
+
+       key4 = buxton_key_create("hello", "test1", "user", INT32);
+       if (!key4) {
+               return -1;
+       }
+
+       if (buxton_register_notification(client, key1, notify_cb, &status, false)) {
+               printf("set call failed to run\n");
+               return -1;
+       }
+
+       if (buxton_register_notification(client, key2, notify_cb, &status, false)) {
+               printf("set call failed to run\n");
+               return -1;
+       }
+
+       if (buxton_register_notification(client, key3, notify_cb, &status, false)) {
+               printf("set call failed to run\n");
+               return -1;
+       }
+
+repoll:
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r < 0) {
+               printf("poll error\n");
+               return -1;
+       } else if (r == 0) {
+               if (do_update >> 1 == 0) {
+                       val++;
+                       if (buxton_set_value(client, key4, &val, set_cb, &status, false)) {
+                               printf("set value failed\n");
+                               return -1;
+                       }
+               } else {
+                       do_update++;
+               }
+               goto repoll;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\n");
+               return -1;
+       }
+
+       if (!status) {
+               printf("Failed to register for notification\n");
+               return -1;
+       }
+
+       goto repoll;
+
+       buxton_key_free(key1);
+       buxton_key_free(key2);
+       buxton_key_free(key3);
+       buxton_close(client);
+
+       return 0;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/demo/timing.c b/demo/timing.c
new file mode 100644 (file)
index 0000000..b374595
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <math.h>
+
+#include "buxton.h"
+#include "util.h"
+
+#define error(...) { printf(__VA_ARGS__); }
+
+static int iterations = 100000;
+
+static void callback(BuxtonResponse response, void *userdata)
+{
+       bool *r;
+
+       if (!userdata) {
+               return;
+       }
+
+       r = (bool *)userdata;
+
+       if (buxton_response_status(response) == 0) {
+               *r = true;
+       }
+}
+
+enum test_type {
+       TEST_GET,
+       TEST_SET,
+       TEST_SET_UNSET,
+       TEST_TYPE_MAX
+};
+
+enum data_type {
+       TEST_INT32,
+       TEST_UINT32,
+       TEST_INT64,
+       TEST_UINT64,
+       TEST_BOOLEAN,
+       TEST_STRING,
+       TEST_STRING4K,
+       TEST_FLOAT,
+       TEST_DOUBLE,
+       TEST_DATA_TYPE_MAX
+};
+
+struct testcase {
+       char name[32];
+       enum test_type t;
+       enum data_type d;
+};
+
+#define TEST_COUNT (TEST_TYPE_MAX * TEST_DATA_TYPE_MAX)
+static struct testcase testcases[TEST_COUNT] = {
+       { "set_int32",          TEST_SET,       TEST_INT32    },
+       { "get_int32",          TEST_GET,       TEST_INT32    },
+       { "set_unset_int32",    TEST_SET_UNSET, TEST_INT32    },
+       { "set_uint32",         TEST_SET,       TEST_UINT32   },
+       { "get_uint32",         TEST_GET,       TEST_UINT32   },
+       { "set_unset_uint32",   TEST_SET_UNSET, TEST_UINT32   },
+       { "set_int64",          TEST_SET,       TEST_INT64    },
+       { "get_int64",          TEST_GET,       TEST_INT64    },
+       { "set_unset_int64",    TEST_SET_UNSET, TEST_INT64    },
+       { "set_uint64",         TEST_SET,       TEST_UINT64   },
+       { "get_uint64",         TEST_GET,       TEST_UINT64   },
+       { "set_unset_uint64",   TEST_SET_UNSET, TEST_UINT64   },
+       { "set_boolean",        TEST_SET,       TEST_BOOLEAN  },
+       { "get_boolean",        TEST_GET,       TEST_BOOLEAN  },
+       { "set_unset_boolean",  TEST_SET_UNSET, TEST_BOOLEAN  },
+       { "set_string",         TEST_SET,       TEST_STRING   },
+       { "get_string",         TEST_GET,       TEST_STRING   },
+       { "set_unset_string",   TEST_SET_UNSET, TEST_STRING   },
+       { "set_string4k",       TEST_SET,       TEST_STRING4K },
+       { "get_string4k",       TEST_GET,       TEST_STRING4K },
+       { "set_unset_string4k", TEST_SET_UNSET, TEST_STRING4K },
+       { "set_float",          TEST_SET,       TEST_FLOAT    },
+       { "get_float",          TEST_GET,       TEST_FLOAT    },
+       { "set_unset_float",    TEST_SET_UNSET, TEST_FLOAT    },
+       { "set_double",         TEST_SET,       TEST_DOUBLE   },
+       { "get_double",         TEST_GET,       TEST_DOUBLE   },
+       { "set_unset_double",   TEST_SET_UNSET, TEST_DOUBLE   }
+};
+
+static BuxtonClient __client;
+static BuxtonData __data;
+static BuxtonKey __key;
+
+static bool init_group(void)
+{
+       BuxtonKey group;
+       bool r;
+       bool d = false;
+
+       group = buxton_key_create("TimingTest", NULL, "user", STRING);
+       r = buxton_create_group(__client, group, callback, &d, true);
+
+       free(group);
+
+       return (!r && d);
+}
+
+static bool testcase_init(struct testcase *tc)
+{
+       char name[64] = "";
+       int32_t i;
+       uint32_t ui;
+       int64_t i6;
+       uint64_t ui6;
+       bool b;
+       char *string;
+       float f;
+       double d;
+       void *value = NULL;
+
+       switch (tc->d) {
+               case TEST_INT32:
+                       sprintf(name, "TimingTest-%d-int32", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", INT32);
+                       i = -672;
+                       value = &i;
+                       break;
+               case TEST_UINT32:
+                       sprintf(name, "TimingTest-%d-uint32", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", UINT32);
+                       ui = 672;
+                       value = &ui;
+                       break;
+               case TEST_INT64:
+                       sprintf(name, "TimingTest-%d-int64", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", INT64);
+                       i6 = -672 * 672;
+                       value = &i6;
+                       break;
+               case TEST_UINT64:
+                       sprintf(name, "TimingTest-%d-uint64", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", UINT64);
+                       ui6 = 672 * 672;
+                       value = &ui6;
+                       break;
+               case TEST_BOOLEAN:
+                       sprintf(name, "TimingTest-%d-boolean", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", BOOLEAN);
+                       b = true;
+                       value = &b;
+                       break;
+               case TEST_STRING:
+                       sprintf(name, "TimingTest-%d-string", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", STRING);
+                       string = "672";
+                       value = string;
+                       break;
+               case TEST_STRING4K:
+                       sprintf(name, "TimingTest-%d-string4k", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", STRING);
+                       string = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+                       value = string;
+                       break;
+               case TEST_FLOAT:
+                       sprintf(name, "TimingTest-%d-float", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", FLOAT);
+                       f = (float)3.14;
+                       value = &f;
+                       break;
+               case TEST_DOUBLE:
+                       sprintf(name, "TimingTest-%d-double", getpid());
+                       __key = buxton_key_create("TimingTest", name, "user", DOUBLE);
+                       d = 3.14;
+                       value = &d;
+                       break;
+               default:
+                       return false;
+       }
+
+       return !buxton_set_value(__client, __key, value, callback, NULL, true);
+}
+
+static bool testcase_cleanup(struct testcase *tc)
+{
+       bool ret = (!buxton_set_value(__client, __key, &__data, callback, NULL, true) &&
+               !buxton_unset_value(__client,  __key, callback, NULL, true));
+       buxton_key_free(__key);
+       return ret;
+}
+
+static bool testcase_run(struct testcase *tc)
+{
+       bool d = false;
+       bool r, s;
+       switch (tc->t) {
+               case TEST_GET:
+                       r = buxton_get_value(__client, __key, callback, &d, true);
+                       return (!r && d);
+               case TEST_SET:
+                       r = buxton_set_value(__client, __key, &__data, callback, &d, true);
+                       return (!r && d);
+               case TEST_SET_UNSET:
+                       r = buxton_set_value(__client, __key, &__data, callback, &d, true);
+                       s = buxton_unset_value(__client, __key, callback, &d, true);
+                       return (!s && !r && d);
+               default:
+                       return false;
+       }
+}
+
+static bool timed_func(unsigned long long *elapsed, struct testcase *tc)
+{
+       struct timespec tsi, tsf;
+       bool ret;
+
+       clock_gettime(CLOCK_MONOTONIC, &tsi);
+       ret = testcase_run(tc);
+       clock_gettime(CLOCK_MONOTONIC, &tsf);
+
+       *elapsed = (unsigned long long)((tsf.tv_nsec - tsi.tv_nsec) + ((tsf.tv_sec - tsi.tv_sec) * 1000000000));
+       return ret;
+}
+
+static void test(struct testcase *tc)
+{
+       unsigned long long elapsed;
+       unsigned long long total;
+       unsigned long long errors = 0;
+       double meansqr;
+       double mean;
+       double sigma;
+       int i;
+
+       total = 0;
+       meansqr = 0.0;
+       mean = 0.0;
+
+       testcase_init(tc);
+
+       for (i = 0; i < iterations; i++) {
+               if (!timed_func(&elapsed, tc)) {
+                       errors++;
+               }
+
+               mean += (double)elapsed;
+               meansqr += (double)elapsed * (double)elapsed;
+               total += elapsed;
+       }
+       mean /= (double)iterations;
+       meansqr /= (double)iterations;
+       sigma = sqrt(meansqr - (mean * mean));
+
+       testcase_cleanup(tc);
+
+       printf("%-24s  %10.3lfus  %10.3lfus  %10llu\n",
+              tc->name, mean / 1000.0, sigma / 1000.0, errors);
+}
+
+int main(int argc, char **argv)
+{
+       int ret = EXIT_SUCCESS;
+       int i;
+
+       if (argc == 2) {
+               iterations = atoi(argv[1]);
+               if (iterations <= 0) {
+                       exit(EXIT_FAILURE);
+               }
+       } else if (argc != 1) {
+               error("Usage: %s [iterations]\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       if (!buxton_open(&__client)) {
+               error("Unable to open BuxtonClient\n");
+               exit(EXIT_FAILURE);
+       }
+
+       init_group();
+
+       printf("Buxton protocol latency timing tool. Using %i iterations per test.\n", iterations);
+       printf("Test Name:                   Average:        Sigma:     Errors:\n");
+
+       for (i = 0; i < TEST_COUNT; i++)
+               test(&testcases[i]);
+
+       buxton_close(__client);
+       exit(ret);
+}
+
diff --git a/docs/LICENSE.MIT b/docs/LICENSE.MIT
new file mode 100644 (file)
index 0000000..ad18db0
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Intel Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/docs/buxton-api.7 b/docs/buxton-api.7
new file mode 100644 (file)
index 0000000..967c31b
--- /dev/null
@@ -0,0 +1,139 @@
+'\" t
+.TH "BUXTON\-API" "7" "" "buxton 1" "buxton\-api"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton\-api \- List of all Buxton API functions
+
+.SH "DESCRIPTION"
+.PP
+This document contains the complete list of buxton API functions,
+each having its own manual page\&.
+
+In addition, there are several "hello world" demo programs in the
+buxton source tree, in the demos/ directory, that demonstrate how to
+use these API functions\&.
+
+.SH "API functions"
+.SS "Client connections"
+.PP
+\fBbuxton_open\fR(3)
+\(em Open a buxton client connection
+.br
+\fBbuxton_close\fR(3)
+\(em Close a buxton client connection
+.br
+
+.SS "BuxtonKey utility functions"
+.PP
+\fBbuxton_key_create\fR(3)
+\(em Create a client\-side key (BuxtonKey)
+.br
+\fBbuxton_key_free\fR(3)
+\(em Free a BuxtonKey
+.br
+\fBbuxton_key_get_type\fR(3)
+\(em Get the type member of a BuxtonKey
+.br
+\fBbuxton_key_get_layer\fR(3)
+\(em Get the layer member of a BuxtonKey
+.br
+\fBbuxton_key_get_group\fR(3)
+\(em Get a group member of a BuxtonKey
+.br
+\fBbuxton_key_get_name\fR(3)
+\(em Get a name member of a BuxtonKey
+.br
+
+.SS "Group storage and manipulation"
+.PP
+\fBbuxton_create_group\fR(3)
+\(em Create a group within a layer
+.br
+\fBbuxton_remove_group\fR(3)
+\(em Remove a group within a layer
+.br
+\fBbuxton_set_label\fR(3)
+\(em Set the Smack label for a group
+.br
+
+.SS "Key storage and manipulation"
+.PP
+\fBbuxton_set_value\fR(3)
+\(em Set the value for a key
+.br
+\fBbuxton_get_value\fR(3)
+\(em Get the value of a key
+.br
+\fBbuxton_unset_value\fR(3)
+\(em Unset the value for a key
+.br
+\fBbuxton_set_label\fR(3)
+\(em Set the Smack label for a key
+.br
+
+.SS "Notifications"
+.PP
+\fBbuxton_register_notification\fR(3)
+\(em Register for a key notification
+.br
+\fBbuxton_unregister_notification\fR(3)
+\(em Unregister for a key notification
+.br
+\fBbuxton_handle_response\fR(3)
+\(em Notification response helper
+.br
+
+.SS "Callbacks"
+.PP
+\fBbuxton_response_status\fR(3)
+\(em Get the status for a BuxtonResponse in a callback
+.br
+\fBbuxton_response_key\fR(3)
+\(em Fetch the BuxtonKey within a callback
+.br
+\fBbuxton_response_type\fR(3)
+\(em Fetch the response type within a callback
+.br
+\fBbuxton_response_value\fR(3)
+\(em Fetch the response value within a callback
+.br
+
+.SS "Configuration"
+.PP
+\fBbuxton_set_conf_file\fR(3)
+\(em Set the path to the Buxton configuration file
+.br
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton-protocol.7 b/docs/buxton-protocol.7
new file mode 100644 (file)
index 0000000..4c0b78d
--- /dev/null
@@ -0,0 +1,136 @@
+'\" t
+.TH "BUXTON\-PROTOCOL" "7" "" "buxton 1" "buxton\-protocol"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton\-protocol \- The wire protocol for buxton
+
+.SH "DESCRIPTION"
+.PP
+A buxton client communicates with the buxton daemon
+(\fBbuxtond\fR(8)) through a Unix Domain Socket by sending
+messages over this socket that adhere to a specific format\&.  Also,
+\fBbuxtond\fR(8) may send a response back to the client in a same
+format\&. The required rules, formats, and values permitted in this
+two-way communication channel is hereafter called the "wire
+protocol"\&.
+
+.SH "PROTOCOL SPECIFICATION"
+.PP
+The following sections detail the specifics of the wire protocol\&.
+
+.SH "MESSAGE FORMAT"
+.PP
+The messages sent to \fBbuxtond\fR(8) from a client, or vice
+versa, adhere to the following format:
+
+.SS "Header"
+.PP
+Magic (2 bytes)
+.RS 4
+The magic byte string has hex value 0x672\&.
+.RE
+.PP
+Control code (2 bytes)
+.RS 4
+All control codes belong to an enum with 13 elements\&. Each code is
+cast to a uint16_t value when serialized\&.
+
+For client messages, the accepted control codes are:
+BUXTON_CONTROL_SET, BUXTON_CONTROL_SET_LABEL,
+BUXTON_CONTROL_CREATE_GROUP, BUXTON_CONTROL_REMOVE_GROUP,
+BUXTON_CONTROL_GET, BUXTON_CONTROL_UNSET, BUXTON_CONTROL_NOTIFY, and
+BUXTON_CONTROL_UNNOTIFY\&.
+
+For daemon responses, accepted control codes are:
+BUXTON_CONTROL_STATUS and BUXTON_CONTROL_CHANGED\&.
+
+.RE
+.PP
+Message size (4 bytes)
+.RS 4
+The size on the entire message, with type uint32_t\&.
+.RE
+.PP
+Message ID (8 bytes)
+.RS 4
+The unique ID for this message, with type uint32_t\&.
+.RE
+.PP
+Parameter count (4 bytes)
+.RS 4
+The number of parameters in the message body, with contents as
+described in the next section\&. This number has type uint32_t\&.
+.RE
+
+.SS "Message body"
+.PP
+The message body contains one or more parameters, each with the
+following structure:
+.PP
+Data type (2 bytes)
+.RS 4
+All recognized data types belong to an enum with 8 elements\&. Each
+type is cast to a uint16_t value when serialized\&.
+
+Accepted types include STRING, INT32, UINT32, INT64, UINT64, FLOAT,
+DOUBLE, and BOOLEAN\&.
+\&.
+.RE
+.PP
+Data length (4 bytes)
+.RS 4
+The length the data value, as defined in the next entry\&.
+.RE
+.PP
+Data value (varies depending on data type)
+.RS 4
+For data with STRING type, the value may range from 0 (zero) bytes to
+an arbitrary maximum, as permitted by the maximum message length and
+factoring in the overall payload of the message\&. For all other data
+types, the value size depends on the data type, with maximum size of
+8 bytes\&.
+.RE
+
+.SH "NOTES"
+.PP
+The maximum message length is 32KB (32768 bytes)\&.
+.PP
+The message byte order is dependent on the endianness of the host
+machine\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtonctl\fR(1),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "FOOTNOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton-security.7 b/docs/buxton-security.7
new file mode 100644 (file)
index 0000000..e59ae2c
--- /dev/null
@@ -0,0 +1,132 @@
+'\" t
+.TH "BUXTON\-SECURITY" "7" "" "buxton 1" "buxton\-security"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton\-security \- Outline of buxton security model
+
+.SH "DESCRIPTION"
+.PP
+Buxton uses a Mandatory Access Control (MAC) system to secure
+configuration storage elements, namely, groups and keys within these
+groups\&. MAC is enforced by using the
+\m[blue]\fBSmack\fR\m[]\&\s-2\u[1]\d\s+2
+Linux Security Module\&.
+
+Each group and key that exists in buxton's configuration storage has
+a Smack label associated with it\&. The label set by buxton is taken
+from the Smack label of the running process (client) that created the
+original group or key\&. If the label for a group or key should be
+changed after initial group or key creation, a client with
+appropriate privilege (UID 0) may modify it using
+\fBbuxton_set_label\fR(3) or the "set\-label" command of
+\fBbuxtonctl\fR(1)\&.
+
+MAC is enforced for nearly all types of access requested by
+clients\&. When enforcement is in effect, buxton consults the list of
+Smack rules, managed by the Smack LSM, to make an access decision\&.
+The table below lists the checks in effect for every buxton
+command\&.
+
+.B Table\ \&1.\ \&Access check grid
+.TS
+allbox tab(:);
+lB lB.
+T{
+Command
+T}:T{
+Access checks
+T}
+.T&
+l l
+l l
+l l
+l l
+l l
+l l.
+T{
+set\-value (int32, bool, etc\&.)
+T}:T{
+Check read/write access on the group, and if the key exists, check
+read/write access on the key\&.
+T}
+T{
+get\-value (int32, bool, etc\&.)
+T}:T{
+Check read access on the group, and check read access on the key\&.
+T}
+T{
+unset\-value
+T}:T{
+Check read/write access on the group and on the key\&.
+T}
+T{
+create\-group
+T}:T{
+For system layers, check for UID 0\&. For user layers, no permission
+checks are needed\&.
+T}
+T{
+remove\-group
+T}:T{
+For system layers, check for UID 0\&. For user layers, check write
+access on the group\&.
+T}
+T{
+set\-label
+T}:T{
+For system layers, check for UID 0\&. Action is not allowed for user
+layers\&.
+T}
+.TE
+
+.sp 1
+
+.PP
+Since buxton uses layers to store sets of groups and keys, and
+identical group/key sets may exist across all layers, fine\-grained
+access control is achievable by setting appropriate Smack labels on
+groups and keys for the targeted layer\&. For a more detailed
+discussion of the interaction of group/key labels and the layer
+model, see \fBbuxton\-layers\fR(7)\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[2]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtonctl\fR(1),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Smack
+.RS 4
+\%https://www.kernel.org/doc/Documentation/security/Smack.txt
+.RE
+.IP " 2." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton.7 b/docs/buxton.7
new file mode 100644 (file)
index 0000000..1e4ff8c
--- /dev/null
@@ -0,0 +1,69 @@
+'\" t
+.TH "BUXTON" "7" "" "buxton 1" "buxton"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton \- A security\-enabled configuration system
+
+.SH "DESCRIPTION"
+.PP
+Buxton is a security\-enabled configuration management system\&. It
+features a layered approach to configuration storage, with each layer
+containing an arbitrary number of groups, each of which may contain
+key\-value pairs\&. Mandatory Access Control (MAC) is implemented at
+the group level and at the key\-value level\&.
+
+Currently, buxton uses \m[blue]\fBSmack\fR\m[]\&\s-2\u[1]\d\s+2 to
+enforce MAC\&.
+
+Buxton provides a C library, documented in \fBbuxton\-api\fR(7), for
+client applications to use\&. Internally, buxton uses
+\fBbuxtond\fR(8) for processing client requests and enforcing
+MAC\&. Also, \fBbuxtonctl\fR(1) is provided for interactive use and
+for use in shell scripts\&.
+
+Minimal examples of client API usage are found in
+\fBbuxton\-examples\fR(7), and an in\-depth overview of buxton is
+found in \fBbuxton\-overview\fR(7)\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[2]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxtonctl\fR(1),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7),
+\fBbuxton\-examples\fR(7),
+\fBbuxton\-overview\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Smack
+.RS 4
+\%https://www.kernel.org/doc/Documentation/security/Smack.txt
+.RE
+.IP " 2." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton.conf.5 b/docs/buxton.conf.5
new file mode 100644 (file)
index 0000000..d3a2485
--- /dev/null
@@ -0,0 +1,126 @@
+'\" t
+.TH "BUXTON\&.CONF" "5" "" "buxton 1" "buxton\&.conf"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton\&.conf \- Configuration of Buxton settings and layer
+information
+
+.SH "SYNOPSIS"
+.PP
+/etc/buxton\&.conf
+
+.SH "DESCRIPTION"
+.PP
+The buxton configuration file can be used to override various paths
+used by \fBbuxtonctl\fR(1) and \fBbuxtond\fR(8), as well as the
+system\-wide definition of buxton layers\&.
+
+.SH "OPTIONS"
+.PP
+Options that are specified in the
+"[Configuration]"
+section:
+.PP
+\fIModuleDirectory=\fR
+.RS 4
+Sets the directory where backend modules are stored\&. The path is
+used by \fBbuxtond\fR(8) to dynamically load the modules as
+needed\&.
+.RE
+.PP
+\fIDatabasePath=\fR
+.RS 4
+Sets the directory where \fBbuxtond\fR(8) (or \fBbuxtonctl\fR(1)
+in direct mode) will store configuration data\&.
+.RE
+.PP
+\fISmackLoadFile=\fR
+.RS 4
+Sets the location of the Smack "load2" file from the Smack virtual
+filesystem\&.
+.RE
+.PP
+\fISocketPath=\fR
+.RS 4
+Sets the path for the Unix Domain Socket used by buxton clients to
+communicate with \fBbuxtond\fR(8)\&.
+.RE
+
+.PP
+Buxton layers are configured in individual sections of the config
+file, where each section name is equivalent to a layer name\&. For
+example, the "base" layer configuration is set in the "[base]"
+section\&.
+
+.PP
+Options that are specified in each layer section:
+.PP
+\fIType=\fR
+.RS 4
+The type of the layer\&. Accepted values are "System" or "User"\&.
+.RE
+.PP
+\fIBackend=\fR
+.RS 4
+The backend to use for the layer\&. Accepted values are "gdbm" or
+"memory"\&.  Note that the "memory" backend is volatile, so
+key\-value pairs will be lost when the \fBbuxtond\fR(8) service
+exits\&.
+.RE
+.PP
+\fIPriority=\fR
+.RS 4
+The priority of the layer\&. Accepted values are integers greater
+than or equal to 0 (zero), where 0 is the lowest\-priority value\&.
+.RE
+.PP
+\fIAccess=\fR
+.RS 4
+The access type of the layer\&. Accepted values are "read\-write" and
+"read\-only"\&. This is an optional field that defaults to "read\-write"\&.
+.RE
+.PP
+\fIDescription=\fR
+.RS 4
+A human\-readable description for the given layer\&.
+.RE
+
+.PP
+More details about buxton layers can be found in \fBbuxton\fR(7)\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtonctl\fR(1),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_client_handle_response.3 b/docs/buxton_client_handle_response.3
new file mode 100644 (file)
index 0000000..c84aa89
--- /dev/null
@@ -0,0 +1,63 @@
+'\" t
+.TH "BUXTON_CLIENT_HANDLE_RESPONSE" "3" "buxton 1" "buxton_client_handle_response"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_client_handle_response \- Notification response helper
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+ssize_t buxton_client_handle_response(BuxtonClient \fIclient\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+This function retrieves the response from \fBbuxtond\fR for the \fIclient\fR.
+
+Several manual pages include code examples that use this function.
+For an example, see \fBbuxton_create_group\fR(3).
+
+.SH "RETURN VALUE"
+.PP
+Returns the number of messages processed, or -1 if there was an error\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_close.3 b/docs/buxton_close.3
new file mode 100644 (file)
index 0000000..f7be2a6
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_open.3
diff --git a/docs/buxton_create_group.3 b/docs/buxton_create_group.3
new file mode 100644 (file)
index 0000000..1c74978
--- /dev/null
@@ -0,0 +1,235 @@
+'\" t
+.TH "BUXTON_CREATE_GROUP" "3" "buxton 1" "buxton_create_group"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_create_group, buxton_remove_group \- Manage groups within buxton
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_create_group(BuxtonClient \fIclient\fB,
+.br
+                        BuxtonKey \fIkey\fB,
+.br
+                        BuxtonCallback \fIcallback\fB,
+.br
+                        void *\fIdata\fB,
+.br
+                        bool \fIsync\fB)
+.sp
+.br
+int buxton_remove_group(BuxtonClient \fIclient\fB,
+.br
+                        BuxtonKey \fIkey\fB,
+.br
+                        BuxtonCallback \fIcallback\fB,
+.br
+                        void *\fIdata\fB,
+.br
+                        bool \fIsync\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used for managing groups within buxton\&.
+
+Before a value can be set for a key-name, the group for the key-name
+must be created\&. A group can be created by calling
+\fBbuxton_create_group\fR(3). Creating a group is done on behalf of
+\fIclient\fR, and the BuxtonKey, \fIkey\fR, is group to be created\&.
+For more information about BuxtonKeys, see
+\fBbuxton_key_create\fR(3)\&.
+
+Groups can also be removed by calling \fBbuxton_remove_group\fR(3)\&.
+Note that this operation is recursive, removing all key-names within
+a group, and the group itself\&.
+
+Both functions accept optional callback functions to register with
+the daemon, referenced by the \fIcallback\fR argument; the callback
+function is called upon completion of the operation\&. The \fIdata\fR
+argument is a pointer to arbitrary userdata that is passed along to
+the callback function\&. Additonally, the \fIsync\fR argument
+controls whether the operation should be synchronous or not; if
+\fIsync\fR is false, the operation is asynchronous\&.
+
+.SH "CODE EXAMPLE"
+.PP
+An example for \fBbuxton_create_group\fR(3):
+
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void create_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to create group\\n");
+       } else {
+               printf("Created group\\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", NULL, "user", STRING);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_create_group(client, key, create_cb,
+                               NULL, false)) {
+               printf("create group call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+An example for \fBbuxton_remove_group\fR(3):
+
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void remove_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to remove group\\n");
+       } else {
+               printf("Removed group\\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", NULL, "user", STRING);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_remove_group(client, key, remove_cb,
+                               NULL, false)) {
+               printf("remove group call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_get_value.3 b/docs/buxton_get_value.3
new file mode 100644 (file)
index 0000000..ac8730c
--- /dev/null
@@ -0,0 +1,158 @@
+'\" t
+.TH "BUXTON_GET_VALUE" "3" "buxton 1" "buxton_get_value"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_get_value \- Get the value of a key\-name
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_get_value(BuxtonClient \fIclient\fB,
+.br
+                     BuxtonKey \fIkey\fB,
+.br
+                     BuxtonCallback \fIcallback\fB,
+.br
+                     void *\fIdata\fB,
+.br
+                     bool \fIsync\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+This function is used to get the value of a key\-name for
+\fIclient\fR. The key\-name is referenced by \fIkey\fR. If the layer
+for \fIkey\fR is NULL, buxton will traverse layers in priority order
+searching for the key-name value, selecting the value for the first
+key\-name found\&. If the argument is non-NULL, the operation will
+target only that layer\&. For more information on creating a
+BuxtonKey to pass for \fIkey\fR, see \fBbuxton_key_create\fR(3)\&.
+
+To retrieve the result of the operation, clients should define a
+callback function, referenced by the \fIcallback\fR argument; the
+callback function is called upon completion of the operation\&. The
+\fIdata\fR argument is a pointer to arbitrary userdata that is passed
+along to the callback function\&. Additonally, the \fIsync\fR
+argument controls whether the operation should be synchronous or not;
+if \fIsync\fR is false, the operation is asynchronous\&.
+
+.SH "CODE EXAMPLE"
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void get_cb(BuxtonResponse response, void *data)
+{
+       int32_t* ret = (int32_t*)data;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to get value\\n");
+               return;
+       }
+
+       *ret = *(int32_t*)buxton_response_value(response);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int32_t gvalue = -1;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_get_value(client, key, get_cb,
+                            &gvalue, false)) {
+               printf("get call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       if (gvalue >= 0) {
+               printf("got value: %d\\n", gvalue);
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_key_create.3 b/docs/buxton_key_create.3
new file mode 100644 (file)
index 0000000..928dc0f
--- /dev/null
@@ -0,0 +1,109 @@
+'\" t
+.TH "BUXTON_KEY_CREATE" "3" "buxton 1" "buxton_key_create"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_key_create, buxton_key_free, buxton_key_get_layer,
+buxton_key_get_type, buxton_key_get_group, buxton_key_get_name \-
+Manage groups and key\-names for buxton clients
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+BuxtonKey buxton_key_create(char *\fIgroup\fB,
+.br
+                            char *\fIname\fB,
+.br
+                            char *\fIlayer\fB,
+.br
+                            BuxtonDataType \fItype\fB)
+.sp
+.br
+char *buxton_key_get_layer(BuxtonKey \fIkey\fB)
+.sp
+.br
+char *buxton_key_get_group(BuxtonKey \fIkey\fB)
+.sp
+.br
+char *buxton_key_get_name(BuxtonKey \fIkey\fB)
+.sp
+.br
+BuxtonDataType buxton_key_get_type(BuxtonKey \fIkey\fB)
+.sp
+.br
+void buxton_key_free(BuxtonKey \fIkey\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used by buxton clients to manage groups and
+key\-names, both represented by a BuxtonKey\&. BuxtonKeys are
+required for all configuration management operations within buxton.
+
+A BuxtonKey is created with \fBbuxton_key_create\fR(3). Its arguments
+serve to initialize the BuxtonKey contents, which include the name of
+the \fIlayer\fR, name of the \fIgroup\fR, name of the key \fIname\fR,
+and the \fItype\fR of the key name\&. Groups must be initialized by
+passing a NULL value for \fIname\fR; similarly, key\-names must be
+initialized by passing non\-NULL arguments for both \fIgroup\fR and
+\fIname\fR\&. For groups, the \fItype\fR is ignored\&. The function
+returns an initialized BuxtonKey\&.
+
+Note that upon creation, a BuxtonKey is either a group or key\-name,
+but not both\&. In other words, if a client needs to a create a group
+and key\-name within that group, two BuxtonKeys need to be created\&.
+
+After creating a BuxtonKey, its layer, group, key name, or key name
+type fields can be requested by calling
+\fBbuxton_key_get_layer\fR(3), \fBbuxton_key_get_group\fR(3),
+\fBbuxton_key_get_name\fR(3), or \fBbuxton_key_get_type\fR(3),
+respectively\&. Each of these functions take a single argument, the
+BuxtonKey that is being queried, and return the value requested\&.
+Note that calling \fBbuxton_key_get_type\fR(3) for a group will
+return STRING, since buxton treats groups as strings internally\&.
+Calling \fBbuxton_key_get_name\fR(3) for a group will return NULL,
+since the name field is not used for groups\&.
+
+All BuxtonKey's are freed automatically on buxton_close, but can
+be freed manually if desired by calling \fBbuxton_key_free\fR(3)\&. 
+This function takes a single argument, the BuxtonKey to be freed\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_key_free.3 b/docs/buxton_key_free.3
new file mode 100644 (file)
index 0000000..04f8c6d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_key_create.3
diff --git a/docs/buxton_key_get_group.3 b/docs/buxton_key_get_group.3
new file mode 100644 (file)
index 0000000..04f8c6d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_key_create.3
diff --git a/docs/buxton_key_get_layer.3 b/docs/buxton_key_get_layer.3
new file mode 100644 (file)
index 0000000..04f8c6d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_key_create.3
diff --git a/docs/buxton_key_get_name.3 b/docs/buxton_key_get_name.3
new file mode 100644 (file)
index 0000000..04f8c6d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_key_create.3
diff --git a/docs/buxton_key_get_type.3 b/docs/buxton_key_get_type.3
new file mode 100644 (file)
index 0000000..04f8c6d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_key_create.3
diff --git a/docs/buxton_open.3 b/docs/buxton_open.3
new file mode 100644 (file)
index 0000000..e7a4d83
--- /dev/null
@@ -0,0 +1,95 @@
+'\" t
+.TH "BUXTON_OPEN" "3" "buxton 1" "buxton_open"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_open, buxton_close \- Manage buxton client connections
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_open(BuxtonClient *\fIclient\fB)
+.sp
+.br
+void buxton_close(BuxtonClient \fIclient\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used to manage connections from a buxton client to the
+buxton daemon, \fBbuxtond\fR(8)\&. Clients must call \fBbuxton_open\fR(3), to
+create a new connection to the daemon\&. Effectively, creating this connection
+registers the client with the daemon, allowing the client to make configuration
+changes, queries, etc\&. This function requires one argument, \fIclient\fR, a
+pointer to a BuxtonClient owned by the client\&. It returns 0 on success,
+and a non-zero status code on failure\&.
+
+To terminate this connection, the client must call \fBbuxton_close\fR(3)\&. The
+required argument is a reference to the same BuxtonClient passed to
+\fBbuxton_open\fR(3)\&.
+
+.SH "CODE EXAMPLE"
+.nf
+.sp
+#define _GNU_SOURCE
+#include <buxton.h>
+
+int main(void)
+{
+       BuxtonClient client;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       /* Manipulate data, register for notifications, ... */
+
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_register_notification.3 b/docs/buxton_register_notification.3
new file mode 100644 (file)
index 0000000..b8302e3
--- /dev/null
@@ -0,0 +1,199 @@
+'\" t
+.TH "BUXTON_REGISTER_NOTIFICATION" "3" "buxton 1" "buxton_register_notification"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_register_notification, buxton_unregister_notification \-
+Manage key-name notifications
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_register_notification(BuxtonClient \fIclient\fB,
+.br
+                                 BuxtonKey \fIkey\fB,
+.br
+                                 BuxtonCallback \fIcallback\fB,
+.br
+                                 void *\fIdata\fB,
+.br
+                                 bool \fIsync\fB)
+.sp
+.br
+int buxton_unregister_notification(BuxtonClient \fIclient\fB,
+.br
+                                   BuxtonKey \fIkey\fB,
+.br
+                                   BuxtonCallback \fIcallback\fB,
+.br
+                                   void *\fIdata\fB,
+.br
+                                   bool \fIsync\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used to manage key\-name notifications on
+\fIkey\fR for \fIclient\fR.
+
+To register for notifications on a specific key\-name, the client
+should call \fBbuxton_register_notification\fR(3)\&. Similarly, to
+unregister for notifications, \fBbuxton_unregister_notification\fR(3)
+can be used\&.
+
+Both functions accept optional callback functions to register with
+the daemon, referenced by the \fIcallback\fR argument; the callback
+function is called upon completion of the operation\&. The \fIdata\fR
+argument is a pointer to arbitrary userdata that is passed along to
+the callback function\&.  Additonally, the \fIsync\fR argument
+controls whether the operation should be synchronous or not; if
+\fIsync\fR is false, the operation is asynchronous\&.
+
+.SH "CODE EXAMPLE"
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "buxton.h"
+
+void notify_cb(BuxtonResponse response, void *data)
+{
+       bool *status = (bool *)data;
+       BuxtonKey key;
+       int32_t *value;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               *status = false;
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+
+       value = (int32_t*)buxton_response_value(response);
+       if (value) {
+               printf("key %s updated with new value %d\\n", name, *value);
+       } else {
+               printf("key %s was removed\\n", name);
+       }
+
+       buxton_key_free(key);
+       free(value);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       bool status = true;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       int repoll_count = 10;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", NULL, INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_register_notification(client, key, notify_cb, &status, false)) {
+               printf("set call failed to run\\n");
+               return -1;
+       }
+repoll:
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r < 0) {
+               printf("poll error\\n");
+               return -1;
+       } else if (r == 0) {
+               if (repoll_count-- > 0) {
+                       goto out;
+               }
+               goto repoll;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       if (!status) {
+               printf("Failed to register for notification\\n");
+               return -1;
+       }
+
+       goto repoll;
+
+out:
+       if (buxton_unregister_notification(client, key, NULL, NULL, true)) {
+               printf("Unregistration of notification failed\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+
+       return 0;
+}
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_remove_group.3 b/docs/buxton_remove_group.3
new file mode 100644 (file)
index 0000000..47ef80d
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_create_group.3
diff --git a/docs/buxton_response_key.3 b/docs/buxton_response_key.3
new file mode 100644 (file)
index 0000000..f1681a0
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_response_status.3
diff --git a/docs/buxton_response_status.3 b/docs/buxton_response_status.3
new file mode 100644 (file)
index 0000000..5c025f6
--- /dev/null
@@ -0,0 +1,94 @@
+'\" t
+.TH "BUXTON_RESPONSE_STATUS" "3" "buxton 1" "buxton_response_status"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_response_status, buxton_response_type, buxton_response_key,
+buxton_response_value \- Query responses from the buxton daemon
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int32_t buxton_reponse_status(BuxtonResponse \fIresponse\fB)
+.sp
+.br
+BuxtonControlMessage buxton_reponse_type(BuxtonResponse \fIresponse\fB)
+.sp
+.br
+BuxtonKey buxton_reponse_key(BuxtonResponse \fIresponse\fB)
+.sp
+.br
+void *buxton_reponse_value(BuxtonResponse \fIresponse\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used within client-defined buxton callback
+functions to query the response returned to the client by the buxton
+daemon, \fBbuxtond\fR(8)\&. Callbacks are registered through several
+of the API functions, such as \fBbuxton_set_value\fR(3) or
+\fBbuxton_get_value\fR(3)\&.
+
+With a callback function, the client should check the response status
+by calling \fBbuxton_response_status\fR(3), with the \fIresponse\fR
+argument passed to the callback\&. This function returns 0 on
+success, or a non-zero value on failure\&.
+
+Next, the client will want to check the type of response received
+from the daemon by calling \fBbuxton_response_type\fR(3)\&. The type
+will correspond to the type of operation that the client originally
+requested\&. There are several possible return values, depending on
+the type of operation; for operations that manipulate or query
+configuration, the values include BUXTON_CONTROL_SET,
+BUXTON_CONTROL_SET_LABEL, BUXTON_CONTROL_CREATE_GROUP,
+BUXTON_CONTROL_REMOVE_GROUP, BUXTON_CONTROL_GET, and
+BUXTON_CONTROL_UNSET; for operations related to notifications, the
+values include BUXTON_CONTROL_NOTIFY and BUXTON_CONTROL_UNNOTIFY\&.
+
+Finally, the client can validate that the configuration action
+requested by the client matches the action taken by the daemon\&. To
+query the BuxtonKey operated on by the daemon, the client should call
+\fBbuxton_response_key\fR(3), which returns the BuxtonKey in
+question\&. To query the value acted on by the daemon for this
+BuxtonKey, a call to \fBbuxton_response_value\fR(3) returns an
+untyped pointer to this value\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_response_type.3 b/docs/buxton_response_type.3
new file mode 100644 (file)
index 0000000..f1681a0
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_response_status.3
diff --git a/docs/buxton_response_value.3 b/docs/buxton_response_value.3
new file mode 100644 (file)
index 0000000..f1681a0
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_response_status.3
diff --git a/docs/buxton_set_conf_file.3 b/docs/buxton_set_conf_file.3
new file mode 100644 (file)
index 0000000..edac44f
--- /dev/null
@@ -0,0 +1,92 @@
+'\" t
+.TH "BUXTON_SET_CONF_FILE" "3" "buxton 1" "buxton_set_conf_file"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_set_conf_file \- Set the path to the Buxton configuration file
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_set_conf_file(char *\fIpath\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+Clients may use a custom Buxton configuration file, referenced by \fIpath\fR\&.
+The settings from the new configuration file take effect immediately\&.
+
+.SH "CODE EXAMPLE"
+.nf
+.sp
+#define _GNU_SOURCE
+#include <buxton.h>
+
+int main(void)
+{
+       BuxtonClient client;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       if (!buxton_set_conf_file("/etc/buxton-custom.conf")) {
+               printf("failed to set conf file\\n");
+               return -1;
+       }
+
+       buxton_close(client);
+       return 0;
+}
+
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\&.conf\fR(5),
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_set_label.3 b/docs/buxton_set_label.3
new file mode 100644 (file)
index 0000000..6f47570
--- /dev/null
@@ -0,0 +1,159 @@
+'\" t
+.TH "BUXTON_SET_LABEL" "3" "buxton 1" "buxton_set_label"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_set_label \- Set the Smack label for a group or key
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_set_label(BuxtonClient \fIclient\fB,
+.br
+                     BuxtonKey \fIkey\fB,
+.br
+                     char *\fIvalue\fB,
+.br
+                     BuxtonCallback \fIcallback\fB,
+.br
+                     void *\fIdata\fB,
+.br
+                     bool \fIsync\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+This function is used to set a Smack label, \fIvalue\fR, on a group
+or key, referred to by \fIkey\fR, on behalf of \fIclient\fR.
+
+Note that setting a Smack label is always a privileged operation, so
+this operation will fail for unprivileged clients\&.
+
+Both functions accept optional callback functions to register with
+the daemon, referenced by the \fIcallback\fR argument; the callback
+function is called upon completion of the operation\&. The \fIdata\fR
+argument is a pointer to arbitrary userdata that is passed along to
+the callback function\&. Additonally, the \fIsync\fR argument
+controls whether the operation should be synchronous or not; if
+\fIsync\fR is false, the operation is asynchronous\&.
+
+.SH "CODE EXAMPLE"
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void set_label_cb(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to set label\\n");
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+       printf("Set label for key %s\\n", name);
+       buxton_key_free(key);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       char* label = "label-test";
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_set_label(client, key, label, set_label_cb,
+                            NULL, false)) {
+               printf("set label call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_set_value.3 b/docs/buxton_set_value.3
new file mode 100644 (file)
index 0000000..3717ba3
--- /dev/null
@@ -0,0 +1,246 @@
+'\" t
+.TH "BUXTON_SET_VALUE" "3" "buxton 1" "buxton_set_value"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxton_set_value, buxton_unset_value \- Modify values for BuxtonKeys
+
+.SH "SYNOPSIS"
+.nf
+\fB
+#include <buxton.h>
+\fR
+.sp
+\fB
+int buxton_set_value(BuxtonClient \fIclient\fB,
+.br
+                     BuxtonKey \fIkey\fB,
+.br
+                     void *\fIvalue\fB,
+.br
+                     BuxtonCallback \fIcallback\fB,
+.br
+                     void *\fIdata\fB,
+.br
+                     bool \fIsync\fB)
+.sp
+.br
+int buxton_unset_value(BuxtonClient \fIclient\fB,
+.br
+                       BuxtonKey \fIkey\fB,
+.br
+                       BuxtonCallback \fIcallback\fB,
+.br
+                       void *\fIdata\fB,
+.br
+                       bool \fIsync\fB)
+\fR
+.fi
+
+.SH "DESCRIPTION"
+.PP
+These functions are used to modify values for a BuxtonKey, referenced
+by \fIkey\fR, on behalf of the \fIclient\fR.
+
+To set the value for a \fIkey\fR, clients should call
+\fBbuxton_set_value\fR(3), passing a pointer the \fIvalue\fR for the
+third argument\&.
+
+To unset the value for a \fIkey\fR, clients should call
+\fBbuxton_unset_value\fR(3)\&.
+
+Both functions accept optional callback functions to register with
+the daemon, referenced by the \fIcallback\fR argument; the callback
+function is called upon completion of the operation\&. The \fIdata\fR
+argument is a pointer to arbitrary userdata that is passed along to
+the callback function\&. Additonally, the \fIsync\fR argument
+controls whether the operation should be synchronous or not; if
+\fIsync\fR is false, the operation is asynchronous\&.
+
+.SH "CODE EXAMPLE"
+.PP
+An example for set_value:
+
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void set_cb(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       char *name;
+
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to set value\\n");
+               return;
+       }
+
+       key = buxton_response_key(response);
+       name = buxton_key_get_name(key);
+       printf("Set value for key %s\\n", name);
+       buxton_key_free(key);
+       free(name);
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+       int32_t set;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       set = 10;
+
+       if (buxton_set_value(client, key, &set, set_cb,
+                            NULL, false)) {
+               printf("set call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.PP
+An example for unset_value:
+
+.nf
+.sp
+#define _GNU_SOURCE
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+
+void unset_cb(BuxtonResponse response, void *data)
+{
+       if (buxton_response_status(response) != 0) {
+               printf("Failed to unset value\\n");
+       } else {
+               printf("Unset value\\n");
+       }
+}
+
+int main(void)
+{
+       BuxtonClient client;
+       BuxtonKey key;
+       struct pollfd pfd[1];
+       int r;
+       int fd;
+
+       if ((fd = buxton_open(&client)) < 0) {
+               printf("couldn't connect\\n");
+               return -1;
+       }
+
+       key = buxton_key_create("hello", "test", "user", INT32);
+       if (!key) {
+               return -1;
+       }
+
+       if (buxton_unset_value(client, key, unset_cb,
+                              NULL, false)) {
+               printf("unset call failed to run\\n");
+               return -1;
+       }
+
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r <= 0) {
+               printf("poll error\\n");
+               return -1;
+       }
+
+       if (!buxton_client_handle_response(client)) {
+               printf("bad response from daemon\\n");
+               return -1;
+       }
+
+       buxton_key_free(key);
+       buxton_close(client);
+       return 0;
+}
+.fi
+
+.SH "RETURN VALUE"
+.PP
+Returns 0 on success, and a non\-zero value on failure\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception
+for code examples found in the \fBCODE EXAMPLE\fR section, which are
+licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR
+file from this buxton distribution\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxton_unregister_notification.3 b/docs/buxton_unregister_notification.3
new file mode 100644 (file)
index 0000000..f3ba4e8
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_register_notification.3
diff --git a/docs/buxton_unset_value.3 b/docs/buxton_unset_value.3
new file mode 100644 (file)
index 0000000..d1eecca
--- /dev/null
@@ -0,0 +1 @@
+.so buxton_set_value.3
diff --git a/docs/buxtonctl.1 b/docs/buxtonctl.1
new file mode 100644 (file)
index 0000000..3dd95a9
--- /dev/null
@@ -0,0 +1,228 @@
+'\" t
+.TH "BUXTONCTL" "1" "" "buxton 1" "buxtonctl"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxtonctl \- Manipulates data stored in the buxton configuration
+system
+
+.SH "SYNOPSIS"
+.HP \w'\fBbuxtonctl\fR\ 'u
+\fBbuxtonctl\fR [OPTIONS...] COMMAND ARGS
+
+.SH "DESCRIPTION"
+.PP
+\fBbuxtonctl\fR
+is used to modify and query configuration, as managed by
+\fBbuxton\fR(7)\&.
+
+.SH "OPTIONS"
+.PP
+The following options are understood:
+.PP
+\fB\-h\fR, \fB\-\-help\fR
+.RS 4
+Prints a help message\&.
+.RE
+.PP
+\fB\-d\fR, \fB\-\-direct\fR
+.RS 4
+Modifies the configuration directly, without connecting to
+\fBbuxtond\fR(8)\&. Note that this is a privileged operation\&.
+.RE
+.PP
+\fB\-c\fR FILE, \fB\-\-config\-file\fR FILE
+.RS 4
+Path to a buxton configuration file (see \fBbuxton\&.conf\fR(5))\&.
+.RE
+
+.SH "COMMANDS"
+.PP
+The following commands are understood:
+.SS "Group manipulation"
+.PP
+\fBcreate\-group\fR LAYER GROUP
+.RS 4
+Creates a group within the specified layer\&.
+.RE
+.PP
+\fBset\-label\fR LAYER GROUP LABEL
+.RS 4
+Sets the Smack label for the group in the specified layer\&. Note
+that this is a privileged operation\&.
+.RE
+.PP
+\fBremove\-group\fR LAYER GROUP
+.RS 4
+Removes a group within the specified layer\&. Note that this
+operation recursively removes all keys within the given group\&.
+.RE
+.SS "Key manipulation"
+.PP
+Note that all "get" commands accept an optional LAYER argument\&.
+Without specifying the layer, the returned value will be taken from
+the layer with highest priority that the client has permission to
+read\&. With the layer argument, buxton will try to return the key
+value only from the given layer\&.
+.PP
+\fBget\-string\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with string type\&.
+.RE
+.PP
+\fBset\-string\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with string type\&.
+.RE
+.PP
+\fBget\-int32\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with int32_t type\&.
+.RE
+.PP
+\fBset\-int32\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with string type\&.
+.RE
+.PP
+\fBget\-uint32\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with uint32_t type\&.
+.RE
+.PP
+\fBset\-uint32\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with uint32_t type\&.
+.RE
+.PP
+\fBget\-int64\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with int64_t type\&.
+.RE
+.PP
+\fBset\-int64\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with int64_t type\&.
+.RE
+.PP
+\fBget\-uint64\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with uint64_t type\&.
+.RE
+.PP
+\fBset\-uint64\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with uint64_t type\&.
+.RE
+.PP
+\fBget\-float\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with float type\&.
+.RE
+.PP
+\fBset\-float\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with float type\&.
+.RE
+.PP
+\fBget\-double\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with double type\&.
+.RE
+.PP
+\fBset\-double\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with double type\&.
+.RE
+.PP
+\fBget\-bool\fR [LAYER] GROUP KEY
+.RS 4
+Gets a key value with boolean type\&.
+.RE
+.PP
+\fBset\-bool\fR LAYER GROUP KEY VALUE
+.RS 4
+Sets a key value with boolean type\&.
+.RE
+.PP
+\fBset\-label\fR LAYER GROUP KEY LABEL
+.RS 4
+Sets the Smack label on a key\&. Note that this is a privileged
+operation\&.
+.RE
+.PP
+\fBunset\-value\fR LAYER GROUP KEY
+.RS 4
+Unset the value on a key\&. This removes the key from the given
+group\&.
+.RE
+
+.SH "ENVIRONMENT VARIABLES"
+.PP
+\fI$BUXTON_CONF_FILE\fR
+.RS 4
+The path to a buxton configuration file (see
+\fBbuxton\&.conf\fR(5))\&.
+.RE
+.PP
+\fI$BUXTON_MODULE_DIR\fR
+.RS 4
+The directory in which buxton's backend modules reside\&.
+.RE
+.PP
+\fI$BUXTON_DB_PATH\fR
+.RS 4
+The directory that buxtond will use for configuration storage\&.
+.RE
+.PP
+\fI$BUXTON_SMACK_LOAD_FILE\fR
+.RS 4
+The path to the Smack "load2" file, typically residing on the Smack
+virtual filesystem (smackfs)\&.
+.RE
+.PP
+\fI$BUXTON_BUXTON_SOCKET\fR
+.RS 4
+The path to the Unix Domain Socket used by buxton clients to
+communicate with buxtond\&.
+.RE
+
+.SH "EXIT STATUS"
+.PP
+On success, 0 is returned, a non\-zero failure code otherwise\&.
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtond\fR(8),
+\fBbuxton\-api\fR(7),
+\fBbuxton\&.conf\fR(5)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/docs/buxtond.8 b/docs/buxtond.8
new file mode 100644 (file)
index 0000000..0bd8042
--- /dev/null
@@ -0,0 +1,95 @@
+'\" t
+.TH "BUXTOND" "8" "" "buxton 1" "buxtond"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+buxtond \- The buxton daemon
+
+.SH "SYNOPSIS"
+.HP \w'\fBbuxtond\fR\ 'u
+\fBbuxtond\fR [OPTIONS...]
+
+.SH "DESCRIPTION"
+.PP
+\fBbuxtond\fR is the system service used by buxton to handle client
+requests and enforce Mandatory Access Control\&.
+
+.SH "OPTIONS"
+.PP
+The following options are understood:
+.PP
+\fB\-h\fR, \fB\-\-help\fR
+.RS 4
+Prints a help message\&.
+.RE
+.PP
+\fB\-c\fR FILE, \fB\-\-config\-file\fR FILE
+.RS 4
+Path to a buxton configuration file (see \fBbuxton\&.conf\fR(5))\&.
+.RE
+
+.SH "ENVIRONMENT VARIABLES"
+.PP
+\fI$BUXTON_CONF_FILE\fR
+.RS 4
+The path to a buxton configuration file (see
+\fBbuxton\&.conf\fR(5))\&.
+.RE
+.PP
+\fI$BUXTON_MODULE_DIR\fR
+.RS 4
+The directory in which buxton's backend modules reside\&.
+.RE
+.PP
+\fI$BUXTON_DB_PATH\fR
+.RS 4
+The directory that buxtond will use for configuration storage\&.
+.RE
+.PP
+\fI$BUXTON_SMACK_LOAD_FILE\fR
+.RS 4
+The path to the Smack "load2" file, typically residing on the Smack
+virtual filesystem (smackfs)\&.
+.RE
+.PP
+\fI$BUXTON_BUXTON_SOCKET\fR
+.RS 4
+The path to the Unix Domain Socket used by buxton clients to
+communicate with buxtond\&.
+.RE
+
+.SH "COPYRIGHT"
+.PP
+Copyright 2014 Intel Corporation\&. License: Creative Commons
+Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&.
+
+.SH "SEE ALSO"
+.PP
+\fBbuxton\fR(7),
+\fBbuxtonctl\fR(1),
+\fBbuxton\-api\fR(7),
+\fBbuxton\&.conf\fR(5)
+
+.SH "NOTES"
+.IP " 1." 4
+Creative Commons Attribution\-ShareAlike 3.0 Unported
+.RS 4
+\%http://creativecommons.org/licenses/by-sa/3.0/
+.RE
diff --git a/m4/.empty b/m4/.empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/packaging/buxton.manifest b/packaging/buxton.manifest
new file mode 100644 (file)
index 0000000..1a9c577
--- /dev/null
@@ -0,0 +1,11 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+       <assign>
+               <filesystem path="/bin/*" label="_" exec_label="none" />
+               <filesystem path="/sbin/*" label="_" exec_label="none" />
+               <filesystem path="/usr/bin/*" label="_" exec_label="none" />
+               <filesystem path="/usr/sbin/*" label="_" exec_label="none" />
+       </assign>
+</manifest>
diff --git a/packaging/buxton.spec b/packaging/buxton.spec
new file mode 100644 (file)
index 0000000..9875644
--- /dev/null
@@ -0,0 +1,82 @@
+Name:    buxton
+Version: 2
+Release: 1
+License: LGPL-2.1+ and MIT
+Group:   Framework/system
+Summary: buxton
+Source0: %{name}-%{version}.tar.bz2
+Source1001: %{name}.manifest
+
+BuildRequires: pkgconfig(check)
+BuildRequires: pkgconfig(libsystemd-daemon)
+BuildRequires: gdbm-devel
+BuildRequires: libattr-devel
+Requires: %{name}-libs = %{version}-%{release}
+Requires: gdbm
+
+%description
+buxton
+
+%package libs
+Summary:  buxton libraries
+License:  LGPL-2.1
+
+%description libs
+buxton devel
+
+%package devel
+Summary:  buxton devel
+License:  LGPL-2.1
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+buxton devel
+
+%prep
+%setup -q
+
+%build
+cp %{SOURCE1001} .
+./autogen.sh --with-user=root --disable-manpages
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+# Make sure this directory exist
+mkdir -p %{buildroot}%{_sharedstatedir}/buxton
+
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/license
+cat LICENSE.LGPL2.1 > $RPM_BUILD_ROOT%{_datadir}/license/buxton
+cat LICENSE.LGPL2.1 > $RPM_BUILD_ROOT%{_datadir}/license/buxton-libs
+
+%post
+mkdir -p %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d
+ln -s %{_libdir}/systemd/system/buxton.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
+
+%files
+%defattr(-,root,root,-)
+%{_datadir}/license/buxton
+%dir %{_sharedstatedir}/buxton
+%{_sysconfdir}/buxton.conf
+%{_bindir}/buxtonctl
+%{_libdir}/systemd/system/buxton.service
+%{_libdir}/systemd/system/buxton.socket
+%{_libdir}/systemd/system/sockets.target.wants/buxton.socket
+%{_sbindir}/buxtond
+%manifest %{name}.manifest
+
+%files libs
+%defattr(-,root,root,-)
+%{_datadir}/license/buxton-libs
+%{_libdir}/libbuxton.so.*
+%{_libdir}/buxton/gdbm.so
+%{_libdir}/buxton/memory.so
+%manifest %{name}.manifest
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/buxton.h
+%{_libdir}/libbuxton.so
+%{_libdir}/pkgconfig/libbuxton.pc
+%manifest %{name}.manifest
diff --git a/src/cli/client.c b/src/cli/client.c
new file mode 100644 (file)
index 0000000..decb4dd
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+#include "buxtonarray.h"
+#include "buxtonresponse.h"
+#include "client.h"
+#include "direct.h"
+#include "hashmap.h"
+#include "protocol.h"
+#include "util.h"
+
+static char *nv(char *s)
+{
+       if (s) {
+               return s;
+       }
+       return "(null)";
+}
+
+bool cli_create_db(BuxtonControl *control,
+                  __attribute__((unused)) BuxtonDataType type,
+                  char *one,
+                  __attribute__((unused)) char *two,
+                  __attribute__((unused)) char *three,
+                  __attribute__((unused)) char *four)
+{
+       BuxtonString layer_name;
+       bool ret;
+
+       if (!control->client.direct) {
+               printf("Unable to create db in non direct mode\n");
+       }
+
+       layer_name = buxton_string_pack(one);
+
+       ret = buxton_direct_init_db(control, &layer_name);
+
+       return ret;
+}
+
+bool cli_set_label(BuxtonControl *control, BuxtonDataType type,
+                  char *one, char *two, char *three, char *four)
+{
+       BuxtonString label;
+       BuxtonKey key;
+       bool ret = false;
+
+       if (four != NULL) {
+               key = buxton_key_create(two, three, one, type);
+       } else {
+               key = buxton_key_create(two, NULL, one, type);
+       }
+
+       if (!key) {
+               return ret;
+       }
+
+       if (four != NULL) {
+               label = buxton_string_pack(four);
+       } else {
+               label = buxton_string_pack(three);
+       }
+
+       if (control->client.direct) {
+               ret = buxton_direct_set_label(control, (_BuxtonKey *)key, &label);
+       } else {
+               ret = !buxton_set_label(&control->client, key, label.value,
+                                       NULL, NULL, true);
+       }
+
+       if (!ret) {
+               char *name = get_name(key);
+               printf("Failed to update key \'%s:%s\' label in layer '%s'\n",
+                      two, nv(name), one);
+               free(name);
+       }
+       buxton_key_free(key);
+       return ret;
+}
+
+bool cli_create_group(BuxtonControl *control, BuxtonDataType type,
+                     char *one, char *two, char *three, char *four)
+{
+       BuxtonKey key;
+       bool ret = false;
+
+       key = buxton_key_create(two, NULL, one, type);
+       if (!key) {
+               return ret;
+       }
+
+       if (control->client.direct) {
+               ret = buxton_direct_create_group(control, (_BuxtonKey *)key, NULL);
+       } else {
+               ret = !buxton_create_group(&control->client, key, NULL, NULL, true);
+       }
+
+       if (!ret) {
+               char *group = get_group(key);
+               printf("Failed to create group \'%s\' in layer '%s'\n",
+                      nv(group), one);
+               free(group);
+       }
+       buxton_key_free(key);
+       return ret;
+}
+
+bool cli_remove_group(BuxtonControl *control, BuxtonDataType type,
+                     char *one, char *two, char *three, char *four)
+{
+       BuxtonKey key;
+       bool ret = false;
+
+       key = buxton_key_create(two, NULL, one, type);
+       if (!key) {
+               return ret;
+       }
+
+       if (control->client.direct) {
+               ret = buxton_direct_remove_group(control, (_BuxtonKey *)key, NULL);
+       } else {
+               ret = !buxton_remove_group(&control->client, key, NULL, NULL, true);
+       }
+
+       if (!ret) {
+               char *group = get_group(key);
+               printf("Failed to remove group \'%s\' in layer '%s'\n",
+                      nv(group), one);
+               free(group);
+       }
+       buxton_key_free(key);
+       return ret;
+}
+
+bool cli_get_label(BuxtonControl *control, BuxtonDataType type,
+                  char *one, char *two, char *three,
+                  __attribute__((unused)) char *four)
+{
+       /* Not yet implemented */
+       return false;
+}
+
+bool cli_set_value(BuxtonControl *control, BuxtonDataType type,
+                  char *one, char *two, char *three, char *four)
+{
+       BuxtonString value;
+       BuxtonKey key;
+       BuxtonData set;
+       bool ret = false;
+
+       memzero((void*)&set, sizeof(BuxtonData));
+       key = buxton_key_create(two, three, one, type);
+       if (!key) {
+               return ret;
+       }
+
+       value.value = four;
+       value.length = (uint32_t)strlen(four) + 1;
+
+       set.type = type;
+       switch (set.type) {
+       case STRING:
+               set.store.d_string.value = value.value;
+               set.store.d_string.length = value.length;
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               four, NULL, NULL, true);
+               }
+               break;
+       case INT32:
+               set.store.d_int32 = (int32_t)strtol(four, NULL, 10);
+               if (errno) {
+                       printf("Invalid int32_t value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_int32, NULL,
+                                               NULL, true);
+               }
+               break;
+       case UINT32:
+               set.store.d_uint32 = (uint32_t)strtol(value.value, NULL, 10);
+               if (errno) {
+                       printf("Invalid uint32_t value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_uint32, NULL,
+                                               NULL, true);
+               }
+               break;
+       case INT64:
+               set.store.d_int64 = strtoll(value.value, NULL, 10);
+               if (errno) {
+                       printf("Invalid int64_t value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_int64, NULL,
+                                               NULL, true);
+               }
+               break;
+       case UINT64:
+               set.store.d_uint64 = strtoull(value.value, NULL, 10);
+               if (errno) {
+                       printf("Invalid uint64_t value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_uint64, NULL,
+                                               NULL, true);
+               }
+               break;
+       case FLOAT:
+               set.store.d_float = strtof(value.value, NULL);
+               if (errno) {
+                       printf("Invalid float value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_float, NULL,
+                                               NULL, true);
+               }
+               break;
+       case DOUBLE:
+               set.store.d_double = strtod(value.value, NULL);
+               if (errno) {
+                       printf("Invalid double value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_double, NULL,
+                                               NULL, true);
+               }
+               break;
+       case BOOLEAN:
+               if (strcaseeq(value.value, "true") ||
+                   strcaseeq(value.value, "on") ||
+                   strcaseeq(value.value, "enable") ||
+                   strcaseeq(value.value, "yes") ||
+                   strcaseeq(value.value, "y") ||
+                   strcaseeq(value.value, "t") ||
+                   strcaseeq(value.value, "1")) {
+                       set.store.d_boolean = true;
+               } else if (strcaseeq(value.value, "false") ||
+                        strcaseeq(value.value, "off") ||
+                        strcaseeq(value.value, "disable") ||
+                        strcaseeq(value.value, "no") ||
+                        strcaseeq(value.value, "n") ||
+                        strcaseeq(value.value, "f") ||
+                        strcaseeq(value.value, "0")) {
+                       set.store.d_boolean = false;
+               } else {
+                       printf("Invalid bool value\n");
+                       return ret;
+               }
+               if (control->client.direct) {
+                       ret = buxton_direct_set_value(control,
+                                                     (_BuxtonKey *)key,
+                                                     &set, NULL);
+               } else {
+                       ret = !buxton_set_value(&control->client, key,
+                                               &set.store.d_boolean,
+                                               NULL, NULL, true);
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (!ret) {
+               char *group = get_group(key);
+               char *name = get_name(key);
+               char *layer = get_layer(key);
+
+               printf("Failed to update key \'%s:%s\' in layer '%s'\n",
+                      nv(group), nv(name), nv(layer));
+               free(group);
+               free(name);
+               free(layer);
+       }
+
+       return ret;
+}
+
+void get_value_callback(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       BuxtonData *r = (BuxtonData *)data;
+       void *p;
+
+       if (buxton_response_status(response) != 0) {
+               return;
+       }
+
+       p = buxton_response_value(response);
+       if (!p) {
+               return;
+       }
+       key = buxton_response_key(response);
+       if (!key) {
+               free(p);
+               return;
+       }
+
+       switch (buxton_key_get_type(key)) {
+       case STRING:
+               r->store.d_string.value = (char *)p;
+               r->store.d_string.length = (uint32_t)strlen(r->store.d_string.value) + 1;
+               r->type = STRING;
+               break;
+       case INT32:
+               r->store.d_int32 = *(int32_t *)p;
+               r->type = INT32;
+               break;
+       case UINT32:
+               r->store.d_uint32 = *(uint32_t *)p;
+               r->type = UINT32;
+               break;
+       case INT64:
+               r->store.d_int64 = *(int64_t *)p;
+               r->type = INT64;
+               break;
+       case UINT64:
+               r->store.d_uint64 = *(uint64_t *)p;
+               r->type = UINT64;
+               break;
+       case FLOAT:
+               r->store.d_float = *(float *)p;
+               r->type = FLOAT;
+               break;
+       case DOUBLE:
+               memcpy(&r->store.d_double, p, sizeof(double));
+               r->type = DOUBLE;
+               break;
+       case BOOLEAN:
+               r->store.d_boolean = *(bool *)p;
+               r->type = BOOLEAN;
+               break;
+       default:
+               break;
+       }
+
+       if (buxton_key_get_type(key) != STRING) {
+               free(p);
+       }
+       free(key);
+}
+
+bool cli_get_value(BuxtonControl *control, BuxtonDataType type,
+                  char *one, char *two, char *three, __attribute__((unused)) char * four)
+{
+       BuxtonKey key;
+       BuxtonData get;
+       _cleanup_free_ char *prefix = NULL;
+       _cleanup_free_ char *group = NULL;
+       _cleanup_free_ char *name = NULL;
+       BuxtonString dlabel;
+       bool ret = false;
+       int32_t ret_val;
+       int r;
+
+       memzero((void*)&get, sizeof(BuxtonData));
+       if (three != NULL) {
+               key = buxton_key_create(two, three, one, type);
+               r = asprintf(&prefix, "[%s] ", one);
+               if (!r) {
+                       abort();
+               }
+       } else {
+               key = buxton_key_create(one, two, NULL, type);
+               r = asprintf(&prefix, " ");
+               if (!r) {
+                       abort();
+               }
+       }
+
+       if (!key) {
+               return false;
+       }
+
+       if (three != NULL) {
+               if (control->client.direct) {
+                       ret = buxton_direct_get_value_for_layer(control, key,
+                                                               &get, &dlabel,
+                                                               NULL);
+               } else {
+                       ret = buxton_get_value(&control->client,
+                                                     key,
+                                                     get_value_callback,
+                                                     &get, true);
+               }
+               if (ret) {
+                       group = get_group(key);
+                       name = get_name(key);
+                       printf("Requested key was not found in layer \'%s\': %s:%s\n",
+                              one, nv(group), nv(name));
+                       return false;
+               }
+       } else {
+               if (control->client.direct) {
+                       ret_val = buxton_direct_get_value(control, key, &get, &dlabel, NULL);
+                       if (ret_val == 0) {
+                               ret = true;
+                       }
+               } else {
+                       ret = buxton_get_value(&control->client, key,
+                                                     get_value_callback, &get,
+                                                     true);
+               }
+               if (ret) {
+                       group = get_group(key);
+                       name = get_name(key);
+                       printf("Requested key was not found: %s:%s\n", nv(group),
+                              nv(name));
+                       return false;
+               }
+       }
+
+       group = get_group(key);
+       name = get_name(key);
+       switch (get.type) {
+       case STRING:
+               printf("%s%s:%s = %s\n", prefix, nv(group), nv(name),
+                      get.store.d_string.value ? get.store.d_string.value : "");
+               break;
+       case INT32:
+               printf("%s%s:%s = %" PRId32 "\n", prefix, nv(group),
+                      nv(name), get.store.d_int32);
+               break;
+       case UINT32:
+               printf("%s%s:%s = %" PRIu32 "\n", prefix, nv(group),
+                      nv(name), get.store.d_uint32);
+               break;
+       case INT64:
+               printf("%s%s:%s = %" PRId64 "\n", prefix, nv(group),
+                      nv(name), get.store.d_int64);
+               break;
+       case UINT64:
+               printf("%s%s:%s = %" PRIu64 "\n", prefix, nv(group),
+                      nv(name), get.store.d_uint64);
+               break;
+       case FLOAT:
+               printf("%s%s:%s = %f\n", prefix, nv(group),
+                      nv(name), get.store.d_float);
+               break;
+       case DOUBLE:
+               printf("%s%s:%s = %f\n", prefix, nv(group),
+                      nv(name), get.store.d_double);
+               break;
+       case BOOLEAN:
+               if (get.store.d_boolean == true) {
+                       printf("%s%s:%s = true\n", prefix, nv(group),
+                              nv(name));
+               } else {
+                       printf("%s%s:%s = false\n", prefix, nv(group),
+                              nv(name));
+               }
+               break;
+       case BUXTON_TYPE_MIN:
+               printf("Requested key was not found: %s:%s\n", nv(group),
+                              nv(name));
+               return false;
+       default:
+               printf("unknown type\n");
+               return false;
+       }
+
+       if (get.type == STRING) {
+               free(get.store.d_string.value);
+       }
+       return true;
+}
+
+bool cli_list_keys(BuxtonControl *control,
+                  __attribute__((unused))BuxtonDataType type,
+                  char *one, char *two, char *three,
+                  __attribute__((unused)) char *four)
+{
+       /* not yet implemented */
+       return false;
+}
+
+void unset_value_callback(BuxtonResponse response, void *data)
+{
+       BuxtonKey key = buxton_response_key(response);
+       char *group, *name;
+
+       if (!key) {
+               return;
+       }
+
+       group = buxton_key_get_group(key);
+       name = buxton_key_get_name(key);
+       printf("unset key %s:%s\n", nv(group), nv(name));
+
+       free(group);
+       free(name);
+       buxton_key_free(key);
+}
+
+bool cli_unset_value(BuxtonControl *control,
+                    BuxtonDataType type,
+                    char *one, char *two, char *three,
+                    __attribute__((unused)) char *four)
+{
+       BuxtonKey key;
+
+       key = buxton_key_create(two, three, one, type);
+
+       if (!key) {
+               return false;
+       }
+
+       if (control->client.direct) {
+               return buxton_direct_unset_value(control, key, NULL);
+       } else {
+               return !buxton_unset_value(&control->client,
+                                          key, unset_value_callback,
+                                          NULL, true);
+       }
+}
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/cli/client.h b/src/cli/client.h
new file mode 100644 (file)
index 0000000..05adc36
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file client.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used for buxtonctl
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "backend.h"
+#include "hashmap.h"
+
+/**
+ * Store a command reference in Buxton CLI
+ * @param type Type of data to operate on
+ * @param one Pointer to char for first parameter
+ * @param two Pointer to char for second parameter
+ * @param three Pointer to char for third parameter
+ * @param four Pointer to char for fourth parameter
+ * @return a boolean value, indicating success of the operation
+ */
+typedef bool (*command_method) (BuxtonControl *control, BuxtonDataType type,
+                               char *one, char *two, char *three,
+                               char *four);
+
+/**
+ * Defines a command within the buxtonctl cli
+ */
+typedef struct Command {
+       const char     *name; /**<name of the command*/
+       const char     *description; /**<one line description of the command*/
+       unsigned int   min_arguments; /**<minimum number of arguments */
+       unsigned int   max_arguments; /**<maximum number of arguments */
+       const char     *usage; /**<correct usage of the command */
+       command_method method; /**<pointer to a method */
+       BuxtonDataType type; /**<type of data to operate on */
+} Command;
+
+/**
+ * Create an initialized, empty db
+ * @param control An initialized control structure
+ * @param type Unused
+ * @param one Layer of label being set
+ * @param two Unused
+ * @param three Unused
+ * @param four Unused
+ * @returns bool indicating success or failure
+ */
+bool cli_create_db(BuxtonControl *control,
+                  BuxtonDataType type,
+                  char *one,
+                  char *two,
+                  char *three,
+                  char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Set a label in Buxton
+ * @param control An initialized control structure
+ * @param type Type of label being set (unused)
+ * @param one Layer of label being set
+ * @param two Group of the label being set
+ * @param three Name of the label to set
+ * @param four Label of the label to set
+ * @returns bool indicating success or failure
+ */
+bool cli_set_label(BuxtonControl *control,
+                  __attribute__((unused)) BuxtonDataType type,
+                  char *one, char *two, char *three, char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Create a group in Buxton
+ * @param control An initialized control structure
+ * @param type Type of group being created (unused)
+ * @param one Layer for the group being created
+ * @param two Name of the group
+ * @returns bool indicating success or failure
+ */
+bool cli_create_group(BuxtonControl *control,
+                     __attribute__((unused)) BuxtonDataType type,
+                     char *one, char *two,
+                     __attribute__((unused)) char *three,
+                     __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Remove a group in Buxton
+ * @param control An initialized control structure
+ * @param type Type of group being removed (unused)
+ * @param one Layer for the group being removed
+ * @param two Name of the group
+ * @returns bool indicating success or failure
+ */
+bool cli_remove_group(BuxtonControl *control,
+                     __attribute__((unused)) BuxtonDataType type,
+                     char *one, char *two,
+                     __attribute__((unused)) char *three,
+                     __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get a label from Buxton
+ * @param control An initialized control structure
+ * @param type Type of label being sought (unused)
+ * @param one Layer of the label being sought
+ * @param two Group of the label being sought
+ * @param two Name of the label being sought
+ * @param four NULL (unused)
+ * @returns bool indicating success or failure
+ */
+bool cli_get_label(BuxtonControl *control,
+                  __attribute__((unused)) BuxtonDataType type,
+                  char *one, char *two, char *three,
+                  __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Set a value in Buxton
+ * @param control An initialized control structure
+ * @param type Type of data being set
+ * @param one Layer of data being set
+ * @param two Group of the data being set
+ * @param three Name of the data to set
+ * @param three Value of the data to set
+ * @returns bool indicating success or failure
+ */
+bool cli_set_value(BuxtonControl *control, BuxtonDataType type, char *one,
+                  char *two, char *three, char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get a value from Buxton
+ * @param control An initialized control structure
+ * @param type Type of data being sought
+ * @param one Layer or Group of data being set
+ * @param two  or key of data being set
+ * @param two Key if one is Layer
+ * @param three NULL (unused)
+ * @returns bool indicating success or failure
+ */
+bool cli_get_value(BuxtonControl *control, BuxtonDataType type, char *one,
+                  char *two, char *three,
+                  __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/*
+ * List keys for a layer in Buxton
+ * @param control An initialized control structure
+ * @param type Type of data (unused)
+ * @param one Layer to query
+ * @param two NULL (unused)
+ * @param three NULL (unusued)
+ * @param four NULL (unused)
+ * @returns bool indicating success or failure
+ */
+bool cli_list_keys(BuxtonControl *control,
+                  __attribute__((unused))BuxtonDataType type,
+                  char *one, char *two, char *three,
+                  __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/**
+ * Unset a value from Buxton
+ * @param control An initialized control structure
+ * @param type Type of value (unused)
+ * @param one Layer of data being unset
+ * @param two Group of key to unset
+ * @param three Key of value to unset
+ * @param four NULL (unused)
+ * @returns bool indicating success or failure
+ */
+bool cli_unset_value(BuxtonControl *control,
+                    __attribute__((unused))BuxtonDataType type,
+                    char *one, char *two, char *three,
+                    __attribute__((unused)) char *four)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/cli/main.c b/src/cli/main.c
new file mode 100644 (file)
index 0000000..97788b8
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+/**
+ * \file cli/main.c Buxton command line interface
+ *
+ * Provides a CLI to Buxton through which a variety of operations can be
+ * carried out
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "buxton.h"
+#include "backend.h"
+#include "client.h"
+#include "configurator.h"
+#include "direct.h"
+#include "hashmap.h"
+#include "protocol.h"
+#include "util.h"
+
+static Hashmap *commands;
+static BuxtonControl control;
+
+static void print_version(void)
+{
+       printf("buxtonctl " PACKAGE_VERSION "\n"
+              "Copyright (C) 2013 Intel Corporation\n"
+              "buxton is free software; you can redistribute it and/or modify\n"
+              "it under the terms of the GNU Lesser General Public License as\n"
+              "published by the Free Software Foundation; either version 2.1\n"
+              "of the License, or (at your option) any later version.\n");
+}
+
+static bool print_help(void)
+{
+       const char *key;
+       Iterator iterator;
+       Command *command;
+
+       printf("buxtonctl: Usage\n\n");
+
+       HASHMAP_FOREACH_KEY(command, key, commands, iterator) {
+               printf("\t%12s - %s\n", key, command->description);
+       };
+
+       return true;
+}
+
+static void print_usage(Command *command)
+{
+       if (command->min_arguments == command->max_arguments) {
+               printf("%s takes %d arguments - %s\n", command->name, command->min_arguments, command->usage);
+       } else {
+               printf("%s takes at least %d arguments - %s\n", command->name, command->min_arguments, command->usage);
+       }
+}
+
+/**
+ * Entry point into buxtonctl
+ * @param argc Number of arguments passed
+ * @param argv An array of string arguments
+ * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE
+ */
+int main(int argc, char **argv)
+{
+       bool ret = false;
+       Command c_get_string, c_set_string;
+       Command c_get_int32, c_set_int32;
+       Command c_get_uint32, c_set_uint32;
+       Command c_get_int64, c_set_int64;
+       Command c_get_uint64, c_set_uint64;
+       Command c_get_float, c_set_float;
+       Command c_get_double, c_set_double;
+       Command c_get_bool, c_set_bool;
+       Command c_set_label;
+       Command c_create_group, c_remove_group;
+       Command c_unset_value;
+       Command c_create_db;
+       Command *command;
+       int i = 0;
+       int c;
+       bool help = false;
+       bool version = false;
+       control.client.direct = false;
+       char *conf_path = NULL;
+       BuxtonClient client = NULL;
+
+       /* libtool bites my twinkie */
+       include_configurator();
+       include_protocol();
+       include_serialize();
+
+       /* Build a command list */
+       commands = hashmap_new(string_hash_func, string_compare_func);
+       if (!commands) {
+               exit(EXIT_FAILURE);
+       }
+
+       /* Strings */
+       c_get_string = (Command) { "get-string", "Get a string value by key",
+                                  2, 3, "[layer] group name", &cli_get_value, STRING };
+       hashmap_put(commands, c_get_string.name, &c_get_string);
+
+       c_set_string = (Command) { "set-string", "Set a key with a string value",
+                                  4, 4, "layer group name value", &cli_set_value, STRING };
+       hashmap_put(commands, c_set_string.name, &c_set_string);
+
+       /* 32bit Integers */
+       c_get_int32 = (Command) { "get-int32", "Get an int32_t value by key",
+                                 2, 3, "[layer] group name", &cli_get_value, INT32 };
+       hashmap_put(commands, c_get_int32.name, &c_get_int32);
+
+       c_set_int32 = (Command) { "set-int32", "Set a key with an int32_t value",
+                                 4, 4, "layer group name value", &cli_set_value, INT32 };
+       hashmap_put(commands, c_set_int32.name, &c_set_int32);
+
+       /* Unsigned 32bit Integers */
+       c_get_uint32 = (Command) { "get-uint32", "Get an uint32_t value by key",
+                                  2, 3, "[layer] group name", &cli_get_value, UINT32 };
+       hashmap_put(commands, c_get_uint32.name, &c_get_uint32);
+
+       c_set_uint32 = (Command) { "set-uint32", "Set a key with an uint32_t value",
+                                  4, 4, "layer group name value", &cli_set_value, UINT32 };
+       hashmap_put(commands, c_set_uint32.name, &c_set_uint32);
+
+       /* 32bit Integers */
+       c_get_int64 = (Command) { "get-int64", "Get an int64_t value by key",
+                                 2, 3, "[layer] group name", &cli_get_value, INT64};
+       hashmap_put(commands, c_get_int64.name, &c_get_int64);
+
+       c_set_int64 = (Command) { "set-int64", "Set a key with an int64_t value",
+                                 4, 4, "layer group name value", &cli_set_value, INT64 };
+       hashmap_put(commands, c_set_int64.name, &c_set_int64);
+
+       /* Unsigned 32bit Integers */
+       c_get_uint64 = (Command) { "get-uint64", "Get an uint64_t value by key",
+                                  2, 3, "[layer] group name", &cli_get_value, UINT64};
+       hashmap_put(commands, c_get_uint64.name, &c_get_uint64);
+
+       c_set_uint64 = (Command) { "set-uint64", "Set a key with an uint64_t value",
+                                  4, 4, "layer group name value", &cli_set_value, UINT64 };
+       hashmap_put(commands, c_set_uint64.name, &c_set_uint64);
+
+       /* Floats */
+       c_get_float = (Command) { "get-float", "Get a float point value by key",
+                                 2, 3, "[layer] group name", &cli_get_value, FLOAT };
+       hashmap_put(commands, c_get_float.name, &c_get_float);
+
+       c_set_float = (Command) { "set-float", "Set a key with a floating point value",
+                                 4, 4, "layer group name value", &cli_set_value, FLOAT };
+       hashmap_put(commands, c_set_float.name, &c_set_float);
+
+       /* Doubles */
+       c_get_double = (Command) { "get-double", "Get a double precision value by key",
+                                  2, 3, "[layer] group name", &cli_get_value, DOUBLE };
+       hashmap_put(commands, c_get_double.name, &c_get_double);
+
+       c_set_double = (Command) { "set-double", "Set a key with a double precision value",
+                                  4, 4, "layer group name value", &cli_set_value, DOUBLE };
+       hashmap_put(commands, c_set_double.name, &c_set_double);
+
+       /* Booleans */
+       c_get_bool = (Command) { "get-bool", "Get a boolean value by key",
+                                2, 3, "[layer] group name", &cli_get_value, BOOLEAN };
+       hashmap_put(commands, c_get_bool.name, &c_get_bool);
+
+       c_set_bool = (Command) { "set-bool", "Set a key with a boolean value",
+                                4, 4, "layer group name value", &cli_set_value, BOOLEAN };
+       hashmap_put(commands, c_set_bool.name, &c_set_bool);
+
+       /* SMACK labels */
+       c_set_label = (Command) { "set-label", "Set a value's label",
+                                 3, 4, "layer group [name] label", &cli_set_label, STRING };
+
+       hashmap_put(commands, c_set_label.name, &c_set_label);
+
+       /* Group management */
+       c_create_group = (Command) { "create-group", "Create a group in a layer",
+                                    2, 2, "layer group", &cli_create_group, STRING };
+       hashmap_put(commands, c_create_group.name, &c_create_group);
+
+       c_remove_group = (Command) { "remove-group", "Remove a group from a layer",
+                                    2, 2, "layer group", &cli_remove_group, STRING };
+       hashmap_put(commands, c_remove_group.name, &c_remove_group);
+
+       /* Unset value */
+       c_unset_value = (Command) { "unset-value", "Unset a value by key",
+                                   3, 3, "layer group name", &cli_unset_value, STRING };
+       hashmap_put(commands, c_unset_value.name, &c_unset_value);
+
+       /* Create db for layer */
+       c_create_db = (Command) { "create-db", "Create the database file for a layer",
+                                   1, 1, "layer", &cli_create_db, STRING };
+       hashmap_put(commands, c_create_db.name, &c_create_db);
+
+       static struct option opts[] = {
+               { "config-file", 1, NULL, 'c' },
+               { "direct",      0, NULL, 'd' },
+               { "help",        0, NULL, 'h' },
+               { "version", 0, NULL, 'v' },
+               { NULL, 0, NULL, 0 }
+       };
+
+       while (true) {
+               c = getopt_long(argc, argv, "c:dvh", opts, &i);
+
+               if (c == -1) {
+                       break;
+               }
+
+               switch (c) {
+               case 'c':
+                       conf_path = strdup(optarg);
+                       if (!conf_path) {
+                               goto end;
+                       }
+                       break;
+               case 'd':
+                       control.client.direct = true;
+                       break;
+               case 'v':
+                       version = true;
+                       break;
+               case 'h':
+                       help = true;
+                       break;
+               }
+       }
+
+       if (version) {
+               print_version();
+               goto end;
+       }
+
+       if (optind == argc) {
+               print_help();
+               goto end;
+       }
+
+       if ((command = hashmap_get(commands, argv[optind])) == NULL) {
+               printf("Unknown command: %s\n", argv[optind]);
+               goto end;
+       }
+
+       /* We now execute the command */
+       if (command->method == NULL) {
+               printf("Not yet implemented: %s\n", command->name);
+               goto end;
+       }
+
+       if (help) {
+               /* Ensure we cleanup and abort when using help */
+               print_usage(command);
+               ret = false;
+               goto end;
+       }
+
+       if ((argc - optind - 1 < command->min_arguments) ||
+           (argc - optind - 1 > command->max_arguments)) {
+               print_usage(command);
+               print_help();
+               ret = false;
+               goto end;
+       }
+
+       control.client.uid = geteuid();
+       if (!control.client.direct) {
+               if (conf_path) {
+                       if (buxton_set_conf_file(conf_path)) {
+                               printf("Failed to set configuration file path\n");
+                       }
+               }
+               if (buxton_open(&client) < 0) {
+                       control.client.direct = true;
+               } else {
+                       control.client = *(_BuxtonClient *)client;
+               }
+       }
+
+       if (control.client.direct) {
+               if (conf_path) {
+                       int r;
+                       struct stat st;
+
+                       r = stat(conf_path, &st);
+                       if (r == -1) {
+                               printf("Invalid configuration file path\n");
+                               goto end;
+                       } else {
+                               if (st.st_mode & S_IFDIR) {
+                                       printf("Configuration file given is a directory\n");
+                                       goto end;
+                               }
+                       }
+                       buxton_add_cmd_line(CONFIG_CONF_FILE, conf_path);
+               }
+               if (!buxton_direct_open(&(control))) {
+                       printf("Failed to directly talk to Buxton\n");
+                       ret = false;
+                       goto end;
+               }
+       }
+
+       /* Connected to buxton_client, execute method */
+       ret = command->method(&control, command->type,
+                             optind + 1 < argc ? argv[optind + 1] : NULL,
+                             optind + 2 < argc ? argv[optind + 2] : NULL,
+                             optind + 3 < argc ? argv[optind + 3] : NULL,
+                             optind + 4 < argc ? argv[optind + 4] : NULL);
+
+end:
+       free(conf_path);
+       hashmap_free(commands);
+       if (control.client.direct) {
+               buxton_direct_close(&control);
+       } else {
+               if (client) {
+                       buxton_close(client);
+               }
+       }
+
+       if (ret) {
+               return EXIT_SUCCESS;
+       }
+       return EXIT_FAILURE;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/core/daemon.c b/src/core/daemon.c
new file mode 100644 (file)
index 0000000..ee08b82
--- /dev/null
@@ -0,0 +1,1134 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <attr/xattr.h>
+
+#include "daemon.h"
+#include "direct.h"
+#include "log.h"
+#include "util.h"
+#include "buxtonlist.h"
+
+bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list,
+               _BuxtonKey *key, BuxtonData **value)
+{
+       switch (msg) {
+       case BUXTON_CONTROL_SET:
+               if (count != 4) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING ||
+                   list[2].type != STRING || list[3].type == BUXTON_TYPE_MIN ||
+                   list[3].type == BUXTON_TYPE_MAX) {
+                       return false;
+               }
+               key->layer = list[0].store.d_string;
+               key->group = list[1].store.d_string;
+               key->name = list[2].store.d_string;
+               key->type = list[3].type;
+               *value = &(list[3]);
+               break;
+       case BUXTON_CONTROL_SET_LABEL:
+               if (count == 3) {
+                       if (list[0].type != STRING || list[1].type != STRING ||
+                           list[2].type != STRING) {
+                               return false;
+                       }
+                       key->type = STRING;
+                       key->layer = list[0].store.d_string;
+                       key->group = list[1].store.d_string;
+                       *value = &list[2];
+               } else if (count == 4) {
+                       if (list[0].type != STRING || list[1].type != STRING ||
+                           list[2].type != STRING || list[3].type != STRING) {
+                               return false;
+                       }
+                       key->type = STRING;
+                       key->layer = list[0].store.d_string;
+                       key->group = list[1].store.d_string;
+                       key->name = list[2].store.d_string;
+                       *value = &list[3];
+               } else {
+                       return false;
+               }
+               break;
+       case BUXTON_CONTROL_CREATE_GROUP:
+               if (count != 2) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING) {
+                       return false;
+               }
+               key->type = STRING;
+               key->layer = list[0].store.d_string;
+               key->group = list[1].store.d_string;
+               break;
+       case BUXTON_CONTROL_REMOVE_GROUP:
+               if (count != 2) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING) {
+                       return false;
+               }
+               key->type = STRING;
+               key->layer = list[0].store.d_string;
+               key->group = list[1].store.d_string;
+               break;
+       case BUXTON_CONTROL_GET:
+               if (count == 4) {
+                       if (list[0].type != STRING || list[1].type != STRING ||
+                           list[2].type != STRING || list[3].type != UINT32) {
+                               return false;
+                       }
+                       key->layer = list[0].store.d_string;
+                       key->group = list[1].store.d_string;
+                       key->name = list[2].store.d_string;
+                       key->type = list[3].store.d_uint32;
+               } else if (count == 3) {
+                       if (list[0].type != STRING || list[1].type != STRING ||
+                           list[2].type != UINT32) {
+                               return false;
+                       }
+                       key->group = list[0].store.d_string;
+                       key->name = list[1].store.d_string;
+                       key->type = list[2].store.d_uint32;
+               } else {
+                       return false;
+               }
+               break;
+       case BUXTON_CONTROL_LIST:
+               return false;
+               if (count != 1) {
+                       return false;
+               }
+               if (list[0].type != STRING) {
+                       return false;
+               }
+               *value = &list[0];
+               break;
+       case BUXTON_CONTROL_UNSET:
+               if (count != 4) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING ||
+                   list[2].type != STRING || list[3].type != UINT32) {
+                       return false;
+               }
+               key->layer = list[0].store.d_string;
+               key->group = list[1].store.d_string;
+               key->name = list[2].store.d_string;
+               key->type = list[3].store.d_uint32;
+               break;
+       case BUXTON_CONTROL_NOTIFY:
+               if (count != 3) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING ||
+                   list[2].type != UINT32) {
+                       return false;
+               }
+               key->group = list[0].store.d_string;
+               key->name = list[1].store.d_string;
+               key->type = list[2].store.d_uint32;
+               break;
+       case BUXTON_CONTROL_UNNOTIFY:
+               if (count != 3) {
+                       return false;
+               }
+               if (list[0].type != STRING || list[1].type != STRING ||
+                   list[2].type != UINT32) {
+                       return false;
+               }
+               key->group = list[0].store.d_string;
+               key->name = list[1].store.d_string;
+               key->type = list[2].store.d_uint32;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t size)
+{
+       BuxtonControlMessage msg;
+       int32_t response;
+       BuxtonData *list = NULL;
+       _cleanup_buxton_data_ BuxtonData *data = NULL;
+       uint16_t i;
+       ssize_t p_count;
+       size_t response_len;
+       BuxtonData response_data, mdata;
+       BuxtonData *value = NULL;
+       _BuxtonKey key = {{0}, {0}, {0}, 0};
+       BuxtonArray *out_list = NULL, *key_list = NULL;
+       _cleanup_free_ uint8_t *response_store = NULL;
+       uid_t uid;
+       bool ret = false;
+       uint32_t msgid = 0;
+       uint32_t n_msgid = 0;
+
+       assert(self);
+       assert(client);
+
+       uid = self->buxton.client.uid;
+       p_count = buxton_deserialize_message((uint8_t*)client->data, &msg, size,
+                                            &msgid, &list);
+       if (p_count < 0) {
+               if (errno == ENOMEM) {
+                       abort();
+               }
+               /* Todo: terminate the client due to invalid message */
+               buxton_debug("Failed to deserialize message\n");
+               goto end;
+       }
+
+       /* Check valid range */
+       if (msg <= BUXTON_CONTROL_MIN || msg >= BUXTON_CONTROL_MAX) {
+               goto end;
+       }
+
+       if (!parse_list(msg, (size_t)p_count, list, &key, &value)) {
+               goto end;
+       }
+
+       /* use internal function from buxtond */
+       switch (msg) {
+       case BUXTON_CONTROL_SET:
+               set_value(self, client, &key, value, &response);
+               break;
+       case BUXTON_CONTROL_SET_LABEL:
+               set_label(self, client, &key, value, &response);
+               break;
+       case BUXTON_CONTROL_CREATE_GROUP:
+               create_group(self, client, &key, &response);
+               break;
+       case BUXTON_CONTROL_REMOVE_GROUP:
+               remove_group(self, client, &key, &response);
+               break;
+       case BUXTON_CONTROL_GET:
+               data = get_value(self, client, &key, &response);
+               break;
+       case BUXTON_CONTROL_UNSET:
+               unset_value(self, client, &key, &response);
+               break;
+       case BUXTON_CONTROL_LIST:
+               key_list = list_keys(self, client, &value->store.d_string,
+                                    &response);
+               break;
+       case BUXTON_CONTROL_NOTIFY:
+               register_notification(self, client, &key, msgid, &response);
+               break;
+       case BUXTON_CONTROL_UNNOTIFY:
+               n_msgid = unregister_notification(self, client, &key, &response);
+               break;
+       default:
+               goto end;
+       }
+       /* Set a response code */
+       response_data.type = INT32;
+       response_data.store.d_int32 = response;
+       out_list = buxton_array_new();
+       if (!out_list) {
+               abort();
+       }
+       if (!buxton_array_add(out_list, &response_data)) {
+               abort();
+       }
+
+
+       switch (msg) {
+               /* TODO: Use cascading switch */
+       case BUXTON_CONTROL_SET:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize set response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_SET_LABEL:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize set_label response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_CREATE_GROUP:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize create_group response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_REMOVE_GROUP:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize remove_group response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_GET:
+               if (data && !buxton_array_add(out_list, data)) {
+                       abort();
+               }
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize get response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_UNSET:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize unset response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_LIST:
+               if (key_list) {
+                       for (i = 0; i < key_list->len; i++) {
+                               if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) {
+                                       abort();
+                               }
+                       }
+                       buxton_array_free(&key_list, NULL);
+               }
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize list response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_NOTIFY:
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize notify response message\n");
+                       abort();
+               }
+               break;
+       case BUXTON_CONTROL_UNNOTIFY:
+               mdata.type = UINT32;
+               mdata.store.d_uint32 = n_msgid;
+               if (!buxton_array_add(out_list, &mdata)) {
+                       abort();
+               }
+               response_len = buxton_serialize_message(&response_store,
+                                                       BUXTON_CONTROL_STATUS,
+                                                       msgid, out_list);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize unnotify response message\n");
+                       abort();
+               }
+               break;
+       default:
+               goto end;
+       }
+
+       /* Now write the response */
+       ret = _write(client->fd, response_store, response_len);
+       if (ret) {
+               if (msg == BUXTON_CONTROL_SET && response == 0) {
+                       buxtond_notify_clients(self, client, &key, value);
+               } else if (msg == BUXTON_CONTROL_UNSET && response == 0) {
+                       buxtond_notify_clients(self, client, &key, NULL);
+               }
+       }
+
+end:
+       /* Restore our own UID */
+       self->buxton.client.uid = uid;
+       if (out_list) {
+               buxton_array_free(&out_list, NULL);
+       }
+       if (list) {
+               for (i=0; i < p_count; i++) {
+                       if (list[i].type == STRING) {
+                               free(list[i].store.d_string.value);
+                       }
+               }
+               free(list);
+       }
+       return ret;
+}
+
+void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
+                             _BuxtonKey *key, BuxtonData *value)
+{
+       BuxtonList *list = NULL;
+       BuxtonList *elem = NULL;
+       BuxtonNotification *nitem;
+       _cleanup_free_ uint8_t* response = NULL;
+       size_t response_len;
+       BuxtonArray *out_list = NULL;
+       _cleanup_free_ char *key_name;
+       int r;
+
+       assert(self);
+       assert(client);
+       assert(key);
+
+       r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
+       if (r == -1) {
+               abort();
+       }
+       list = hashmap_get(self->notify_mapping, key_name);
+       if (!list) {
+               return;
+       }
+
+       BUXTON_LIST_FOREACH(list, elem) {
+               nitem = elem->data;
+               int c = 1;
+               __attribute__((unused)) bool unused;
+               free(response);
+               response = NULL;
+
+               if (nitem->old_data && value) {
+                       switch (value->type) {
+                       case STRING:
+                               c = memcmp((const void *)
+                                          (nitem->old_data->store.d_string.value),
+                                          (const void *)(value->store.d_string.value),
+                                          value->store.d_string.length);
+                               break;
+                       case INT32:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_int32),
+                                          (const void *)&(value->store.d_int32),
+                                          sizeof(int32_t));
+                               break;
+                       case UINT32:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
+                                          (const void *)&(value->store.d_uint32),
+                                          sizeof(uint32_t));
+                               break;
+                       case INT64:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_int64),
+                                          (const void *)&(value->store.d_int64),
+                                          sizeof(int64_t));
+                               break;
+                       case UINT64:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
+                                          (const void *)&(value->store.d_uint64),
+                                          sizeof(uint64_t));
+                               break;
+                       case FLOAT:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_float),
+                                          (const void *)&(value->store.d_float),
+                                          sizeof(float));
+                               break;
+                       case DOUBLE:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_double),
+                                          (const void *)&(value->store.d_double),
+                                          sizeof(double));
+                               break;
+                       case BOOLEAN:
+                               c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
+                                          (const void *)&(value->store.d_boolean),
+                                          sizeof(bool));
+                               break;
+                       default:
+                               buxton_log("Internal state corruption: Notification data type invalid\n");
+                               abort();
+                       }
+               }
+
+               if (!c) {
+                       continue;
+               }
+               if (nitem->old_data && (nitem->old_data->type == STRING)) {
+                       free(nitem->old_data->store.d_string.value);
+               }
+               free(nitem->old_data);
+
+               nitem->old_data = malloc0(sizeof(BuxtonData));
+               if (!nitem->old_data) {
+                       abort();
+               }
+               if (value) {
+                       if (!buxton_data_copy(value, nitem->old_data)) {
+                               abort();
+                       }
+               }
+
+               out_list = buxton_array_new();
+               if (!out_list) {
+                       abort();
+               }
+               if (value) {
+                       if (!buxton_array_add(out_list, value)) {
+                               abort();
+                       }
+               }
+
+               response_len = buxton_serialize_message(&response,
+                                                       BUXTON_CONTROL_CHANGED,
+                                                       nitem->msgid, out_list);
+               buxton_array_free(&out_list, NULL);
+               if (response_len == 0) {
+                       if (errno == ENOMEM) {
+                               abort();
+                       }
+                       buxton_log("Failed to serialize notification\n");
+                       abort();
+               }
+               buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
+                            key_name);
+
+               unused = _write(nitem->client->fd, response, response_len);
+       }
+}
+
+void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
+              BuxtonData *value, int32_t *status)
+{
+
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(value);
+       assert(status);
+
+       *status = -1;
+
+       buxton_debug("Daemon setting [%s][%s][%s]\n",
+                    key->layer.value,
+                    key->group.value,
+                    key->name.value);
+
+       self->buxton.client.uid = client->cred.uid;
+
+       if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
+               return;
+       }
+
+       *status = 0;
+       buxton_debug("Daemon set value completed\n");
+}
+
+void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
+              BuxtonData *value, int32_t *status)
+{
+
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(value);
+       assert(status);
+
+       *status = -1;
+
+       buxton_debug("Daemon setting label on [%s][%s][%s]\n",
+                    key->layer.value,
+                    key->group.value,
+                    key->name.value);
+
+       self->buxton.client.uid = client->cred.uid;
+
+       /* Use internal library to set label */
+       if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
+               return;
+       }
+
+       *status = 0;
+       buxton_debug("Daemon set label completed\n");
+}
+
+void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
+                 int32_t *status)
+{
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+
+       buxton_debug("Daemon creating group [%s][%s]\n",
+                    key->layer.value,
+                    key->group.value);
+
+       self->buxton.client.uid = client->cred.uid;
+
+       /* Use internal library to create group */
+       if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
+               return;
+       }
+
+       *status = 0;
+       buxton_debug("Daemon create group completed\n");
+}
+
+void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
+                 int32_t *status)
+{
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+
+       buxton_debug("Daemon removing group [%s][%s]\n",
+                    key->layer.value,
+                    key->group.value);
+
+       self->buxton.client.uid = client->cred.uid;
+
+       /* Use internal library to create group */
+       if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
+               return;
+       }
+
+       *status = 0;
+       buxton_debug("Daemon remove group completed\n");
+}
+
+void unset_value(BuxtonDaemon *self, client_list_item *client,
+                _BuxtonKey *key, int32_t *status)
+{
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+
+       buxton_debug("Daemon unsetting [%s][%s][%s]\n",
+                    key->layer.value,
+                    key->group.value,
+                    key->name.value);
+
+       /* Use internal library to unset value */
+       self->buxton.client.uid = client->cred.uid;
+       if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) {
+               return;
+       }
+
+       buxton_debug("unset value returned successfully from db\n");
+
+       *status = 0;
+       buxton_debug("Daemon unset value completed\n");
+}
+
+BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
+                     _BuxtonKey *key, int32_t *status)
+{
+       BuxtonData *data = NULL;
+       BuxtonString label;
+       int32_t ret;
+
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+
+       data = malloc0(sizeof(BuxtonData));
+       if (!data) {
+               abort();
+       }
+
+       if (key->layer.value) {
+               buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
+                            key->group.value, key->name.value);
+       } else {
+               buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
+                            key->name.value);
+       }
+       self->buxton.client.uid = client->cred.uid;
+       ret = buxton_direct_get_value(&self->buxton, key, data, &label,
+                                     client->smack_label);
+       if (ret) {
+               goto fail;
+       }
+
+       free(label.value);
+       buxton_debug("get value returned successfully from db\n");
+
+       *status = 0;
+       goto end;
+fail:
+       buxton_debug("get value failed\n");
+       free(data);
+       data = NULL;
+end:
+
+       return data;
+}
+
+BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
+                      BuxtonString *layer, int32_t *status)
+{
+       BuxtonArray *ret_list = NULL;
+       assert(self);
+       assert(client);
+       assert(layer);
+       assert(status);
+
+       *status = -1;
+       if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
+               *status = 0;
+       }
+       return ret_list;
+}
+
+void register_notification(BuxtonDaemon *self, client_list_item *client,
+                          _BuxtonKey *key, uint32_t msgid,
+                          int32_t *status)
+{
+       BuxtonList *n_list = NULL;
+       BuxtonNotification *nitem;
+       BuxtonData *old_data = NULL;
+       int32_t key_status;
+       char *key_name;
+       int r;
+
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+
+       nitem = malloc0(sizeof(BuxtonNotification));
+       if (!nitem) {
+               abort();
+       }
+       nitem->client = client;
+
+       /* Store data now, cheap */
+       old_data = get_value(self, client, key, &key_status);
+       if (key_status != 0) {
+               free(nitem);
+               return;
+       }
+       nitem->old_data = old_data;
+       nitem->msgid = msgid;
+
+       /* May be null, but will append regardless */
+       r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
+       if (r == -1) {
+               abort();
+       }
+       n_list = hashmap_get(self->notify_mapping, key_name);
+
+       if (!n_list) {
+               if (!buxton_list_append(&n_list, nitem)) {
+                       abort();
+               }
+
+               if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
+                       abort();
+               }
+       } else {
+               free(key_name);
+               if (!buxton_list_append(&n_list, nitem)) {
+                       abort();
+               }
+       }
+       *status = 0;
+}
+
+uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
+                                _BuxtonKey *key, int32_t *status)
+{
+       BuxtonList *n_list = NULL;
+       BuxtonList *elem = NULL;
+       BuxtonNotification *nitem, *citem = NULL;
+       uint32_t msgid = 0;
+       _cleanup_free_ char *key_name = NULL;
+       void *old_key_name;
+       int r;
+
+       assert(self);
+       assert(client);
+       assert(key);
+       assert(status);
+
+       *status = -1;
+       r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
+       if (r == -1) {
+               abort();
+       }
+       n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
+       /* This key isn't actually registered for notifications */
+       if (!n_list) {
+               return 0;
+       }
+
+       BUXTON_LIST_FOREACH(n_list, elem) {
+               nitem = elem->data;
+               /* Find the list item for this client */
+               if (nitem->client == client) {
+                       citem = nitem;
+               }
+               break;
+       };
+
+       /* Client hasn't registered for notifications on this key */
+       if (!citem) {
+               return 0;
+       }
+
+       msgid = citem->msgid;
+       /* Remove client from notifications */
+       free_buxton_data(&(citem->old_data));
+       buxton_list_remove(&n_list, citem, true);
+
+       /* If we removed the last item, remove the mapping too */
+       if (!n_list) {
+               (void)hashmap_remove(self->notify_mapping, key_name);
+               free(old_key_name);
+       }
+
+       *status = 0;
+
+       return msgid;
+}
+
+bool identify_client(client_list_item *cl)
+{
+       /* Identity handling */
+       ssize_t nr;
+       int data;
+       struct msghdr msgh;
+       struct iovec iov;
+       __attribute__((unused)) struct ucred *ucredp;
+       struct cmsghdr *cmhp;
+       socklen_t len = sizeof(struct ucred);
+       int on = 1;
+
+       assert(cl);
+
+       union {
+               struct cmsghdr cmh;
+               char control[CMSG_SPACE(sizeof(struct ucred))];
+       } control_un;
+
+       setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+       control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+       control_un.cmh.cmsg_level = SOL_SOCKET;
+       control_un.cmh.cmsg_type = SCM_CREDENTIALS;
+
+       msgh.msg_control = control_un.control;
+       msgh.msg_controllen = sizeof(control_un.control);
+
+       msgh.msg_iov = &iov;
+       msgh.msg_iovlen = 1;
+       iov.iov_base = &data;
+       iov.iov_len = sizeof(int);
+
+       msgh.msg_name = NULL;
+       msgh.msg_namelen = 0;
+
+       nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
+       if (nr == -1) {
+               return false;
+       }
+
+       cmhp = CMSG_FIRSTHDR(&msgh);
+
+       if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
+               buxton_log("Invalid cmessage header from kernel\n");
+               abort();
+       }
+
+       if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
+               buxton_log("Missing credentials on socket\n");
+               abort();
+       }
+
+       ucredp = (struct ucred *) CMSG_DATA(cmhp);
+
+       if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
+               buxton_log("Missing label on socket\n");
+               abort();
+       }
+
+       return true;
+}
+
+void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
+{
+       assert(self);
+       assert(fd >= 0);
+
+       if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
+                           (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
+               abort();
+       }
+       if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
+                           (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
+               abort();
+       }
+       self->pollfds[self->nfds].fd = fd;
+       self->pollfds[self->nfds].events = events;
+       self->pollfds[self->nfds].revents = 0;
+       self->accepting[self->nfds] = a;
+       self->nfds++;
+
+       buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
+}
+
+void del_pollfd(BuxtonDaemon *self, nfds_t i)
+{
+       assert(self);
+       assert(i < self->nfds);
+
+       buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
+
+       if (i != (self->nfds - 1)) {
+               memmove(&(self->pollfds[i]),
+                       &(self->pollfds[i + 1]),
+                       /*
+                        * nfds < max int because of kernel limit of
+                        * fds. i + 1 < nfds because of if and assert
+                        * so the casts below are always >= 0 and less
+                        * than long unsigned max int so no loss of
+                        * precision.
+                        */
+                       (size_t)(self->nfds - i - 1) * sizeof(struct pollfd));
+               memmove(&(self->accepting[i]),
+                       &(self->accepting[i + 1]),
+                       (size_t)(self->nfds - i - 1) * sizeof(bool));
+       }
+       self->nfds--;
+}
+
+void handle_smack_label(client_list_item *cl)
+{
+       socklen_t slabel_len = 1;
+       char *buf = NULL;
+       BuxtonString *slabel = NULL;
+       int ret;
+
+       ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len);
+       /* libsmack ignores ERANGE here, so we ignore it too */
+       if (ret < 0 && errno != ERANGE) {
+               switch (errno) {
+               case ENOPROTOOPT:
+                       /* If Smack is not enabled, do not set the client label */
+                       cl->smack_label = NULL;
+                       return;
+               default:
+                       buxton_log("getsockopt(): %m\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       slabel = malloc0(sizeof(BuxtonString));
+       if (!slabel) {
+               abort();
+       }
+
+       /* already checked slabel_len positive above */
+       buf = malloc0((size_t)slabel_len + 1);
+       if (!buf) {
+               abort();
+       }
+
+       ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
+       if (ret < 0) {
+               buxton_log("getsockopt(): %m\n");
+               exit(EXIT_FAILURE);
+       }
+
+       slabel->value = buf;
+       slabel->length = (uint32_t)slabel_len;
+
+       buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
+
+       cl->smack_label = slabel;
+}
+
+bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
+{
+       ssize_t l;
+       uint16_t peek;
+       bool more_data = false;
+       int message_limit = 32;
+
+       assert(self);
+       assert(cl);
+
+       if (!cl->data) {
+               cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
+               cl->offset = 0;
+               cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
+       }
+       if (!cl->data) {
+               abort();
+       }
+       /* client closed the connection, or some error occurred? */
+       if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
+               goto terminate;
+       }
+
+       /* need to authenticate the client? */
+       if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
+               if (!identify_client(cl)) {
+                       goto terminate;
+               }
+
+               handle_smack_label(cl);
+       }
+
+       buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
+
+       /* Hand off any read data */
+       do {
+               l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
+
+               /*
+                * Close clients with read errors. If there isn't more
+                * data and we don't have a complete message just
+                * cleanup and let the client resend their request.
+                */
+               if (l < 0) {
+                       if (errno != EAGAIN) {
+                               goto terminate;
+                       } else {
+                               goto cleanup;
+                       }
+               } else if (l == 0) {
+                       goto cleanup;
+               }
+
+               cl->offset += (size_t)l;
+               if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
+                       continue;
+               }
+               if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) {
+                       cl->size = buxton_get_message_size(cl->data, cl->offset);
+                       if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) {
+                               goto terminate;
+                       }
+               }
+               if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
+                       cl->data = realloc(cl->data, cl->size);
+                       if (!cl->data) {
+                               abort();
+                       }
+               }
+               if (cl->size != cl->offset) {
+                       continue;
+               }
+               if (!buxtond_handle_message(self, cl, cl->size)) {
+                       buxton_log("Communication failed with client %d\n", cl->fd);
+                       goto terminate;
+               }
+
+               message_limit--;
+               if (message_limit) {
+                       cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
+                       cl->offset = 0;
+                       continue;
+               }
+               if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
+                       more_data = true;
+               }
+               goto cleanup;
+       } while (l > 0);
+
+cleanup:
+       free(cl->data);
+       cl->data = NULL;
+       cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
+       cl->offset = 0;
+       return more_data;
+
+terminate:
+       terminate_client(self, cl, i);
+       return more_data;
+}
+
+void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
+{
+       del_pollfd(self, i);
+       close(cl->fd);
+       if (cl->smack_label) {
+               free(cl->smack_label->value);
+       }
+       free(cl->smack_label);
+       free(cl->data);
+       buxton_debug("Closed connection from fd %d\n", cl->fd);
+       LIST_REMOVE(client_list_item, item, self->client_list, cl);
+       free(cl);
+       cl = NULL;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/core/daemon.h b/src/core/daemon.h
new file mode 100644 (file)
index 0000000..7c5f5c0
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file daemon.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used for the buxtond
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include "buxton.h"
+#include "backend.h"
+#include "hashmap.h"
+#include "list.h"
+#include "protocol.h"
+#include "serialize.h"
+
+/**
+ * List for daemon's clients
+ */
+typedef struct client_list_item {
+       LIST_FIELDS(struct client_list_item, item); /**<List type */
+       int fd; /**<File descriptor of connected client */
+       struct ucred cred; /**<Credentials of connected client */
+       BuxtonString *smack_label; /**<Smack label of connected client */
+       uint8_t *data; /**<Data buffer for the client */
+       size_t offset; /**<Current position to write to data buffer */
+       size_t size; /**<Size of the data buffer */
+} client_list_item;
+
+/**
+ * Notification registration
+ */
+typedef struct BuxtonNotification {
+       client_list_item *client; /**<Client */
+       BuxtonData *old_data; /**<Old value of a particular key*/
+       uint32_t msgid; /**<Message id from the client */
+} BuxtonNotification;
+
+/**
+ * Global store of buxtond state
+ */
+typedef struct BuxtonDaemon {
+       size_t nfds_alloc;
+       size_t accepting_alloc;
+       nfds_t nfds;
+       bool *accepting;
+       struct pollfd *pollfds;
+       client_list_item *client_list;
+       Hashmap *notify_mapping;
+       BuxtonControl buxton;
+} BuxtonDaemon;
+
+/**
+ * Take a BuxtonData array and set key, layer and value items
+ * correctly
+ * @param msg Control message specifying how to parse the array
+ * @param count Number of elements in the array
+ * @param list BuxtonData array to parse
+ * @param key Pointer to pointer to set if key is used
+ * @param value Pointer to pointer to set if value is used
+ * @returns bool indicating success of parsing
+ */
+bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list,
+               _BuxtonKey *key, BuxtonData **value)
+       __attribute__((warn_unused_result));
+
+/**
+ * Handle a message within buxtond
+ * @param self Reference to BuxtonDaemon
+ * @param client Current client
+ * @param size Size of the data being handled
+ * @returns bool True if message was successfully handled
+ */
+bool buxtond_handle_message(BuxtonDaemon *self,
+                             client_list_item *client,
+                             size_t size)
+       __attribute__((warn_unused_result));
+
+/**
+ * Notify clients a value changes in buxtond
+ * @param self Refernece to BuxtonDaemon
+ * @param client Current client
+ * @param key Modified key
+ * @param value Modified value
+ */
+void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
+                             _BuxtonKey* key, BuxtonData *value);
+
+/**
+ * Buxton daemon function for setting a value
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key for the value being set
+ * @param value Value being set
+ * @param status Will be set with the int32_t result of the operation
+ */
+void set_value(BuxtonDaemon *self, client_list_item *client,
+              _BuxtonKey *key, BuxtonData *value, int32_t *status);
+
+/**
+ * Buxton daemon function for setting a label
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key or group for the label being set
+ * @param value Label being set
+ * @param status Will be set with the int32_t result of the operation
+ */
+void set_label(BuxtonDaemon *self, client_list_item *client,
+              _BuxtonKey *key, BuxtonData *value, int32_t *status);
+
+/**
+ * Buxton daemon function for creating a group
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key with layer and group members initialized
+ * @param status Will be set with the int32_t result of the operation
+ */
+void create_group(BuxtonDaemon *self, client_list_item *client,
+                 _BuxtonKey *key, int32_t *status);
+
+/**
+ * Buxton daemon function for removing a group
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key with layer and group members initialized
+ * @param status Will be set with the int32_t result of the operation
+ */
+void remove_group(BuxtonDaemon *self, client_list_item *client,
+                 _BuxtonKey *key, int32_t *status);
+
+/**
+ * Buxton daemon function for getting a value
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key for the value being sought
+ * @param status Will be set with the int32_t result of the operation
+ * @returns BuxtonData Value stored for key if successful otherwise NULL
+ */
+BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
+                     _BuxtonKey *key, int32_t *status)
+       __attribute__((warn_unused_result));
+
+/**
+ * Buxton daemon function for unsetting a value
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key for the value being dunset
+ * @param status Will be set with the int32_t result of the operation
+ */
+void unset_value(BuxtonDaemon *self, client_list_item *client,
+                _BuxtonKey *key, int32_t *status);
+
+/**
+ * Buxton daemon function for listing keys in a given layer
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param layer Layer to query
+ * @param status Will be set with the int32_t result of the operation
+ */
+BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
+                      BuxtonString *layer, int32_t *status)
+       __attribute__((warn_unused_result));
+
+/**
+ * Buxton daemon function for registering notifications on a given key
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key to notify for changes on
+ * @param msgid Message ID from the client
+ * @param status Will be set with the int32_t result of the operation
+ */
+void register_notification(BuxtonDaemon *self, client_list_item *client,
+                          _BuxtonKey *key, uint32_t msgid,
+                          int32_t *status);
+
+/**
+ * Buxton daemon function for unregistering notifications from the given key
+ * @param self buxtond instance being run
+ * @param client Used to validate smack access
+ * @param key Key to no longer recieve notifications for
+ * @param status Will be set with the int32_t result of the operation
+ * @return Message ID used to send key's notifications to the client
+ */
+uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
+                                _BuxtonKey *key, int32_t *status)
+       __attribute__((warn_unused_result));
+
+/**
+ * Verify credentials for the client socket
+ * @param cl Client to check the credentials of
+ * @return bool indicating credentials where found or not
+ */
+bool identify_client(client_list_item *cl)
+       __attribute__((warn_unused_result));
+
+/**
+ * Add a fd to daemon's poll list
+ * @param self buxtond instance being run
+ * @param fd File descriptor to add to the poll list
+ * @param events Priority mask for events
+ * @param a Accepting status of the fd
+ * @return None
+ */
+void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a);
+
+/**
+ * Add a fd to daemon's poll list
+ * @param self buxtond instance being run
+ * @param i File descriptor to remove from poll list
+ * @return None
+ */
+void del_pollfd(BuxtonDaemon *self, nfds_t i);
+
+/**
+ * Setup a client's smack label
+ * @param cl Client to set smack label on
+ * @return None
+ */
+void handle_smack_label(client_list_item *cl);
+
+/**
+ * Handle a client connection
+ * @param self buxtond instance being run
+ * @param cl The currently activate client
+ * @param i The currently active file descriptor
+ * @return bool indicating more data to process
+ */
+bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
+       __attribute__((warn_unused_result));
+
+/**
+ * Terminate client connectoin
+ * @param self buxtond instance being run
+ * @param cl The client to terminate
+ * @param i File descriptor to remove from poll list
+ */
+void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/core/main.c b/src/core/main.c
new file mode 100644 (file)
index 0000000..f3ad523
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file core/main.c Buxton daemon
+ *
+ * This file provides the buxton daemon
+ */
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <systemd/sd-daemon.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <attr/xattr.h>
+
+#include "buxton.h"
+#include "backend.h"
+#include "daemon.h"
+#include "direct.h"
+#include "list.h"
+#include "log.h"
+#include "smack.h"
+#include "util.h"
+#include "configurator.h"
+#include "buxtonlist.h"
+
+#define SOCKET_TIMEOUT 5
+
+static BuxtonDaemon self;
+
+static void print_usage(char *name)
+{
+       printf("%s: Usage\n\n", name);
+
+       printf("  -c, --config-file        Path to configuration file\n");
+       printf("  -h, --help               Display this help message\n");
+}
+
+/**
+ * Entry point into buxtond
+ * @param argc Number of arguments passed
+ * @param argv An array of string arguments
+ * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE
+ */
+int main(int argc, char *argv[])
+{
+       int fd;
+       int smackfd = -1;
+       socklen_t addr_len;
+       struct sockaddr_un remote;
+       int descriptors;
+       int ret;
+       bool manual_start = false;
+       sigset_t mask;
+       int sigfd;
+       bool leftover_messages = false;
+       struct stat st;
+       bool help = false;
+       BuxtonList *map_list = NULL;
+       Iterator iter;
+       char *notify_key;
+
+       static struct option opts[] = {
+               { "config-file", 1, NULL, 'c' },
+               { "help",        0, NULL, 'h' },
+               { NULL, 0, NULL, 0 }
+       };
+
+       while (true) {
+               int c;
+               int i;
+               c = getopt_long(argc, argv, "c:h", opts, &i);
+
+               if (c == -1) {
+                       break;
+               }
+
+               switch (c) {
+               case 'c':
+                       ret = stat(optarg, &st);
+                       if (ret == -1) {
+                               buxton_log("Invalid configuration file path\n");
+                               exit(EXIT_FAILURE);
+                       } else {
+                               if (st.st_mode & S_IFDIR) {
+                                       buxton_log("Configuration file given is a directory\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+
+                       buxton_add_cmd_line(CONFIG_CONF_FILE, optarg);
+                       break;
+               case 'h':
+                       help = true;
+                       break;
+               }
+       }
+
+       if (help) {
+               print_usage(argv[0]);
+               exit(EXIT_SUCCESS);
+       }
+
+       if (!buxton_cache_smack_rules()) {
+               exit(EXIT_FAILURE);
+       }
+       smackfd = buxton_watch_smack_rules();
+       if (smackfd < 0 && errno) {
+               exit(EXIT_FAILURE);
+       }
+
+       self.nfds_alloc = 0;
+       self.accepting_alloc = 0;
+       self.nfds = 0;
+       self.buxton.client.direct = true;
+       self.buxton.client.uid = geteuid();
+       if (!buxton_direct_open(&self.buxton)) {
+               exit(EXIT_FAILURE);
+       }
+
+       sigemptyset(&mask);
+       ret = sigaddset(&mask, SIGINT);
+       if (ret != 0) {
+               exit(EXIT_FAILURE);
+       }
+       ret = sigaddset(&mask, SIGTERM);
+       if (ret != 0) {
+               exit(EXIT_FAILURE);
+       }
+       ret = sigaddset(&mask, SIGPIPE);
+       if (ret != 0) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = sigprocmask(SIG_BLOCK, &mask, NULL);
+       if (ret == -1) {
+               exit(EXIT_FAILURE);
+       }
+
+       sigfd = signalfd(-1, &mask, 0);
+       if (sigfd == -1) {
+               exit(EXIT_FAILURE);
+       }
+
+       add_pollfd(&self, sigfd, POLLIN, false);
+
+       /* For client notifications */
+       self.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       /* Store a list of connected clients */
+       LIST_HEAD_INIT(client_list_item, self.client_list);
+
+       descriptors = sd_listen_fds(0);
+       if (descriptors < 0) {
+               buxton_log("sd_listen_fds: %m\n");
+               exit(EXIT_FAILURE);
+       } else if (descriptors == 0) {
+               /* Manual invocation */
+               manual_start = true;
+               union {
+                       struct sockaddr sa;
+                       struct sockaddr_un un;
+               } sa;
+
+               fd = socket(AF_UNIX, SOCK_STREAM, 0);
+               if (fd < 0) {
+                       buxton_log("socket(): %m\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               memzero(&sa, sizeof(sa));
+               sa.un.sun_family = AF_UNIX;
+               strncpy(sa.un.sun_path, buxton_socket(), sizeof(sa.un.sun_path) - 1);
+               sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
+
+               ret = unlink(sa.un.sun_path);
+               if (ret == -1 && errno != ENOENT) {
+                       exit(EXIT_FAILURE);
+               }
+
+               if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
+                       buxton_log("bind(): %m\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               chmod(sa.un.sun_path, 0666);
+
+               if (listen(fd, SOMAXCONN) < 0) {
+                       buxton_log("listen(): %m\n");
+                       exit(EXIT_FAILURE);
+               }
+               add_pollfd(&self, fd, POLLIN | POLLPRI, true);
+       } else {
+               /* systemd socket activation */
+               for (fd = SD_LISTEN_FDS_START + 0; fd < SD_LISTEN_FDS_START + descriptors; fd++) {
+                       if (sd_is_fifo(fd, NULL)) {
+                               add_pollfd(&self, fd, POLLIN, false);
+                               buxton_debug("Added fd %d type FIFO\n", fd);
+                       } else if (sd_is_socket_unix(fd, SOCK_STREAM, -1, buxton_socket(), 0)) {
+                               add_pollfd(&self, fd, POLLIN | POLLPRI, true);
+                               buxton_debug("Added fd %d type UNIX\n", fd);
+                       } else if (sd_is_socket(fd, AF_UNSPEC, 0, -1)) {
+                               add_pollfd(&self, fd, POLLIN | POLLPRI, true);
+                               buxton_debug("Added fd %d type SOCKET\n", fd);
+                       }
+               }
+       }
+
+       if (smackfd >= 0) {
+               /* add Smack rule fd to pollfds */
+               add_pollfd(&self, smackfd, POLLIN | POLLPRI, false);
+       }
+
+       buxton_log("%s: Started\n", argv[0]);
+
+       /* Enter loop to accept clients */
+       for (;;) {
+               ret = poll(self.pollfds, self.nfds, leftover_messages ? 0 : -1);
+
+               if (ret < 0) {
+                       buxton_log("poll(): %m\n");
+                       break;
+               }
+               if (ret == 0) {
+                       if (!leftover_messages) {
+                               continue;
+                       }
+               }
+
+               leftover_messages = false;
+
+               /* check sigfd if the daemon was signaled */
+               if (self.pollfds[0].revents != 0) {
+                       ssize_t sinfo;
+                       struct signalfd_siginfo si;
+
+                       sinfo = read(self.pollfds[0].fd, &si, sizeof(struct signalfd_siginfo));
+                       if (sinfo != sizeof(struct signalfd_siginfo)) {
+                               exit(EXIT_FAILURE);
+                       }
+
+                       if (si.ssi_signo == SIGINT || si.ssi_signo == SIGTERM) {
+                               break;
+                       }
+               }
+
+               for (nfds_t i = 1; i < self.nfds; i++) {
+                       client_list_item *cl = NULL;
+                       char discard[256];
+
+                       if (self.pollfds[i].revents == 0) {
+                               continue;
+                       }
+
+                       if (self.pollfds[i].fd == -1) {
+                               /* TODO: Remove client from list  */
+                               buxton_debug("Removing / Closing client for fd %d\n", self.pollfds[i].fd);
+                               del_pollfd(&self, i);
+                               continue;
+                       }
+
+                       if (smackfd >= 0) {
+                               if (self.pollfds[i].fd == smackfd) {
+                                       if (!buxton_cache_smack_rules()) {
+                                               exit(EXIT_FAILURE);
+                                       }
+                                       buxton_log("Reloaded Smack access rules\n");
+                                       /* discard inotify data itself */
+                                       while (read(smackfd, &discard, 256) == 256);
+                                       continue;
+                               }
+                       }
+
+                       if (self.accepting[i] == true) {
+                               struct timeval tv;
+                               int fd;
+                               int on = 1;
+
+                               addr_len = sizeof(remote);
+
+                               if ((fd = accept(self.pollfds[i].fd,
+                                                (struct sockaddr *)&remote, &addr_len)) == -1) {
+                                       buxton_log("accept(): %m\n");
+                                       break;
+                               }
+
+                               buxton_debug("New client fd %d connected through fd %d\n", fd, self.pollfds[i].fd);
+
+                               if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+                                       close(fd);
+                                       break;
+                               }
+
+                               cl = malloc0(sizeof(client_list_item));
+                               if (!cl) {
+                                       exit(EXIT_FAILURE);
+                               }
+
+                               LIST_INIT(client_list_item, item, cl);
+
+                               cl->fd = fd;
+                               cl->cred = (struct ucred) {0, 0, 0};
+                               LIST_PREPEND(client_list_item, item, self.client_list, cl);
+
+                               /* poll for data on this new client as well */
+                               add_pollfd(&self, cl->fd, POLLIN | POLLPRI, false);
+
+                               /* Mark our packets as high prio */
+                               if (setsockopt(cl->fd, SOL_SOCKET, SO_PRIORITY, &on, sizeof(on)) == -1) {
+                                       buxton_log("setsockopt(SO_PRIORITY): %m\n");
+                               }
+
+                               /* Set socket recv timeout */
+                               tv.tv_sec = SOCKET_TIMEOUT;
+                               tv.tv_usec = 0;
+                               if (setsockopt(cl->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
+                                              sizeof(struct timeval)) == -1) {
+                                       buxton_log("setsockopt(SO_RCVTIMEO): %m\n");
+                               }
+
+                               /* check if this is optimal or not */
+                               break;
+                       }
+
+                       assert(self.accepting[i] == 0);
+                       if (smackfd >= 0) {
+                               assert(self.pollfds[i].fd != smackfd);
+                       }
+
+                       /* handle data on any connection */
+                       /* TODO: Replace with hash table lookup */
+                       LIST_FOREACH(item, cl, self.client_list)
+                               if (self.pollfds[i].fd == cl->fd) {
+                                       break;
+                               }
+
+                       assert(cl);
+                       if (handle_client(&self, cl, i)) {
+                               leftover_messages = true;
+                       }
+               }
+       }
+
+       buxton_log("%s: Closing all connections\n", argv[0]);
+
+       if (manual_start) {
+               unlink(buxton_socket());
+       }
+       for (int i = 0; i < self.nfds; i++) {
+               close(self.pollfds[i].fd);
+       }
+       for (client_list_item *i = self.client_list; i;) {
+               client_list_item *j = i->item_next;
+               free(i);
+               i = j;
+       }
+       /* Clean up notification lists */
+       HASHMAP_FOREACH_KEY(map_list, notify_key, self.notify_mapping, iter) {
+               hashmap_remove(self.notify_mapping, notify_key);
+               BuxtonList *elem;
+               BUXTON_LIST_FOREACH(map_list, elem) {
+                       BuxtonNotification *notif = (BuxtonNotification*)elem->data;
+                       if (notif->old_data) {
+                               free_buxton_data(&(notif->old_data));
+                       }
+               }
+               free(notify_key);
+               buxton_list_free_all(&map_list);
+       }
+
+       hashmap_free(self.notify_mapping);
+       buxton_direct_close(&self.buxton);
+       return EXIT_SUCCESS;
+}
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/db/gdbm.c b/src/db/gdbm.c
new file mode 100644 (file)
index 0000000..e8988c4
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <gdbm.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "hashmap.h"
+#include "serialize.h"
+#include "util.h"
+
+/**
+ * GDBM Database Module
+ */
+
+
+static Hashmap *_resources = NULL;
+
+static char *key_get_name(BuxtonString *key)
+{
+       char *c;
+
+       c = strchr(key->value, 0);
+       if (!c) {
+               return NULL;
+       }
+       if (c - (key->value + (key->length - 1)) >= 0) {
+               return NULL;
+       }
+       c++;
+
+       return c;
+}
+
+static GDBM_FILE try_open_database(char *path, const int oflag)
+{
+       GDBM_FILE db = gdbm_open(path, 0, oflag, S_IRUSR | S_IWUSR, NULL);
+       /* handle open under write mode failing by falling back to
+          reader mode */
+       if (!db && (gdbm_errno == GDBM_FILE_OPEN_ERROR)) {
+               db = gdbm_open(path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL);
+               buxton_debug("Attempting to fallback to opening db as read-only\n");
+               errno = EROFS;
+       } else {
+               if (!db) {
+                       abort();
+               }
+               /* Must do this as gdbm_open messes with errno */
+               errno = 0;
+       }
+       return db;
+}
+
+/* Open or create databases on the fly */
+static GDBM_FILE db_for_resource(BuxtonLayer *layer)
+{
+       GDBM_FILE db;
+       _cleanup_free_ char *path = NULL;
+       char *name = NULL;
+       int r;
+       int oflag = layer->readonly ? GDBM_READER : GDBM_WRCREAT;
+       int save_errno = 0;
+
+       assert(layer);
+       assert(_resources);
+
+       if (layer->type == LAYER_USER) {
+               r = asprintf(&name, "%s-%d", layer->name.value, layer->uid);
+       } else {
+               r = asprintf(&name, "%s", layer->name.value);
+       }
+       if (r == -1) {
+               abort();
+       }
+
+       db = hashmap_get(_resources, name);
+       if (!db) {
+               path = get_layer_path(layer);
+               if (!path) {
+                       abort();
+               }
+
+               db = try_open_database(path, oflag);
+               save_errno = errno;
+               if (!db) {
+                       free(name);
+                       buxton_log("Couldn't create db for path: %s\n", path);
+                       return 0;
+               }
+               r = hashmap_put(_resources, name, db);
+               if (r != 1) {
+                       abort();
+               }
+       } else {
+               db = (GDBM_FILE) hashmap_get(_resources, name);
+               free(name);
+       }
+
+       errno = save_errno;
+       return db;
+}
+
+static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
+                     BuxtonString *label)
+{
+       GDBM_FILE db;
+       int ret = -1;
+       datum key_data;
+       datum cvalue = {0};
+       datum value;
+       _cleanup_free_ uint8_t *data_store = NULL;
+       size_t size;
+       uint32_t sz;
+       BuxtonData cdata = {0};
+       BuxtonString clabel;
+
+       assert(layer);
+       assert(key);
+       assert(label);
+
+       if (key->name.value) {
+               sz = key->group.length + key->name.length;
+               key_data.dptr = malloc(sz);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               /* size is string\0string\0 so just write, bonus for
+                  nil seperator being added without extra work */
+               key_data.dsize = (int)sz;
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               memcpy(key_data.dptr + key->group.length, key->name.value,
+                      key->name.length);
+       } else {
+               key_data.dptr = malloc(key->group.length);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               key_data.dsize = (int)key->group.length;
+       }
+
+       db = db_for_resource(layer);
+       if (!db || errno) {
+               ret = errno;
+               goto end;
+       }
+
+       /* set_label will pass a NULL for data */
+       if (!data) {
+               cvalue = gdbm_fetch(db, key_data);
+               if (cvalue.dsize < 0 || cvalue.dptr == NULL) {
+                       ret = ENOENT;
+                       goto end;
+               }
+
+               data_store = (uint8_t*)cvalue.dptr;
+               buxton_deserialize(data_store, &cdata, &clabel);
+               free(clabel.value);
+               data = &cdata;
+               data_store = NULL;
+       }
+
+       size = buxton_serialize(data, label, &data_store);
+
+       value.dptr = (char *)data_store;
+       value.dsize = (int)size;
+       ret = gdbm_store(db, key_data, value, GDBM_REPLACE);
+       if (ret && gdbm_errno == GDBM_READER_CANT_STORE) {
+               ret = EROFS;
+       }
+       assert(ret == 0);
+
+end:
+       if (cdata.type == STRING) {
+               free(cdata.store.d_string.value);
+       }
+       free(key_data.dptr);
+       free(cvalue.dptr);
+
+       return ret;
+}
+
+static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
+                     BuxtonString *label)
+{
+       GDBM_FILE db;
+       datum key_data;
+       datum value;
+       uint8_t *data_store = NULL;
+       int ret;
+       uint32_t sz;
+
+       assert(layer);
+
+       if (key->name.value) {
+               sz = key->group.length + key->name.length;
+               key_data.dptr = malloc(sz);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               /* size is string\0string\0 so just write, bonus for
+                  nil seperator being added without extra work */
+               key_data.dsize = (int)sz;
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               memcpy(key_data.dptr + key->group.length, key->name.value,
+                      key->name.length);
+       } else {
+               key_data.dptr = malloc(key->group.length);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               key_data.dsize = (int)key->group.length;
+       }
+
+       memzero(&value, sizeof(datum));
+       db = db_for_resource(layer);
+       if (!db) {
+               /*
+                * Set negative here to indicate layer not found
+                * rather than key not found, optimization for
+                * set value
+                */
+               ret = -ENOENT;
+               goto end;
+       }
+
+       value = gdbm_fetch(db, key_data);
+       if (value.dsize < 0 || value.dptr == NULL) {
+               ret = ENOENT;
+               goto end;
+       }
+
+       data_store = (uint8_t*)value.dptr;
+       buxton_deserialize(data_store, data, label);
+
+       if (data->type != key->type) {
+               free(label->value);
+               label->value = NULL;
+               if (data->type == STRING) {
+                       free(data->store.d_string.value);
+                       data->store.d_string.value = NULL;
+               }
+               ret = EINVAL;
+               goto end;
+       }
+       ret = 0;
+
+end:
+       free(key_data.dptr);
+       free(value.dptr);
+       data_store = NULL;
+
+       return ret;
+}
+
+static int unset_value(BuxtonLayer *layer,
+                       _BuxtonKey *key,
+                       __attribute__((unused)) BuxtonData *data,
+                       __attribute__((unused)) BuxtonString *label)
+{
+       GDBM_FILE db;
+       datum key_data;
+       int ret;
+       uint32_t sz;
+
+       assert(layer);
+       assert(key);
+
+       if (key->name.value) {
+               sz = key->group.length + key->name.length;
+               key_data.dptr = malloc(sz);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               /* size is string\0string\0 so just write, bonus for
+                  nil seperator being added without extra work */
+               key_data.dsize = (int)sz;
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               memcpy(key_data.dptr + key->group.length, key->name.value,
+                      key->name.length);
+       } else {
+               key_data.dptr = malloc(key->group.length);
+               if (!key_data.dptr) {
+                       abort();
+               }
+
+               memcpy(key_data.dptr, key->group.value, key->group.length);
+               key_data.dsize = (int)key->group.length;
+       }
+
+       errno = 0;
+       db = db_for_resource(layer);
+       if (!db || gdbm_errno) {
+               ret = EROFS;
+               goto end;
+       }
+
+       ret = gdbm_delete(db, key_data);
+       if (ret) {
+               if (gdbm_errno == GDBM_READER_CANT_DELETE) {
+                       ret = EROFS;
+               } else if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
+                       ret = ENOENT;
+               } else {
+                       abort();
+               }
+       }
+
+end:
+       free(key_data.dptr);
+
+       return ret;
+}
+
+static bool list_keys(BuxtonLayer *layer,
+                     BuxtonArray **list)
+{
+       GDBM_FILE db;
+       datum key, nextkey;
+       BuxtonArray *k_list = NULL;
+       BuxtonData *current = NULL;
+       BuxtonString in_key;
+       char *name;
+       bool ret = false;
+
+       assert(layer);
+
+       db = db_for_resource(layer);
+       if (!db) {
+               goto end;
+       }
+
+       k_list = buxton_array_new();
+       key = gdbm_firstkey(db);
+       /* Iterate through all of the keys */
+       while (key.dptr) {
+               /* Split the key name from the rest of the key */
+               in_key.value = (char*)key.dptr;
+               in_key.length = (uint32_t)key.dsize;
+               name = key_get_name(&in_key);
+               if (!name) {
+                       continue;
+               }
+
+               current = malloc0(sizeof(BuxtonData));
+               if (!current) {
+                       abort();
+               }
+               current->type = STRING;
+               current->store.d_string.value = strdup(name);
+               if (!current->store.d_string.value) {
+                       abort();
+               }
+               current->store.d_string.length = (uint32_t)strlen(name) + 1;
+               if (!buxton_array_add(k_list, current)) {
+                       abort();
+               }
+
+               /* Visit the next key */
+               nextkey = gdbm_nextkey(db, key);
+               free(key.dptr);
+               key = nextkey;
+       }
+
+       /* Pass ownership of the array to the caller */
+       *list = k_list;
+       ret = true;
+
+end:
+       if (!ret && k_list) {
+               for (uint16_t i = 0; i < k_list->len; i++) {
+                       current = buxton_array_get(k_list, i);
+                       if (!current) {
+                               break;
+                       }
+                       free(current->store.d_string.value);
+                       free(current);
+               }
+               buxton_array_free(&k_list, NULL);
+       }
+       return ret;
+}
+
+_bx_export_ void buxton_module_destroy(void)
+{
+       const char *key;
+       Iterator iterator;
+       GDBM_FILE db;
+
+       /* close all gdbm handles */
+       HASHMAP_FOREACH_KEY(db, key, _resources, iterator) {
+               hashmap_remove(_resources, key);
+               gdbm_close(db);
+               free((void *)key);
+       }
+       hashmap_free(_resources);
+       _resources = NULL;
+}
+
+_bx_export_ bool buxton_module_init(BuxtonBackend *backend)
+{
+
+       assert(backend);
+
+       /* Point the struct methods back to our own */
+       backend->set_value = &set_value;
+       backend->get_value = &get_value;
+       backend->list_keys = &list_keys;
+       backend->unset_value = &unset_value;
+       backend->create_db = (module_db_init_func) &db_for_resource;
+
+       _resources = hashmap_new(string_hash_func, string_compare_func);
+       if (!_resources) {
+               abort();
+       }
+
+       return true;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/db/memory.c b/src/db/memory.c
new file mode 100644 (file)
index 0000000..d763390
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+
+#include "hashmap.h"
+#include "log.h"
+#include "buxton.h"
+#include "backend.h"
+#include "util.h"
+
+/**
+ * Memory Database Module
+ *
+ * Used for quick testing and debugging of Buxton, to ensure protocol
+ * and direct access are working as intended.
+ * Note this is not persistent.
+ */
+
+
+static Hashmap *_resources;
+
+/* Return existing hashmap or create new hashmap on the fly */
+static Hashmap *_db_for_resource(BuxtonLayer *layer)
+{
+       Hashmap *db;
+       char *name = NULL;
+       int r;
+
+       assert(layer);
+       assert(_resources);
+
+       if (layer->type == LAYER_USER) {
+               r = asprintf(&name, "%s-%d", layer->name.value, layer->uid);
+       } else {
+               r = asprintf(&name, "%s", layer->name.value);
+       }
+       if (r == -1) {
+               return NULL;
+       }
+
+       db = hashmap_get(_resources, name);
+       if (!db) {
+               db = hashmap_new(string_hash_func, string_compare_func);
+               hashmap_put(_resources, name, db);
+       } else {
+               free(name);
+       }
+
+       return db;
+}
+
+static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
+                     BuxtonString *label)
+{
+       Hashmap *db;
+       BuxtonArray *array = NULL;
+       BuxtonArray *stored;
+       BuxtonData *data_copy = NULL;
+       BuxtonData *d;
+       BuxtonString *label_copy = NULL;
+       BuxtonString *l;
+       char *full_key = NULL;
+       char *k;
+       int ret;
+
+       assert(layer);
+       assert(key);
+       assert(label);
+
+       db = _db_for_resource(layer);
+       if (!db) {
+               ret = ENOENT;
+               goto end;
+       }
+
+       if (key->name.value) {
+               if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) {
+                       abort();
+               }
+       } else {
+               full_key = strdup(key->group.value);
+               if (!full_key) {
+                       abort();
+               }
+       }
+
+       if (!data) {
+               stored = (BuxtonArray *)hashmap_get(db, full_key);
+               if (!stored) {
+                       ret = ENOENT;
+                       free(full_key);
+                       goto end;
+               }
+               data = buxton_array_get(stored, 0);
+       }
+
+       array = buxton_array_new();
+       if (!array) {
+               abort();
+       }
+       data_copy = malloc0(sizeof(BuxtonData));
+       if (!data_copy) {
+               abort();
+       }
+       label_copy = malloc0(sizeof(BuxtonString));
+       if (!label_copy) {
+               abort();
+       }
+
+       if (!buxton_data_copy(data, data_copy)) {
+               abort();
+       }
+       if (!buxton_string_copy(label, label_copy)) {
+               abort();
+       }
+       if (!buxton_array_add(array, data_copy)) {
+               abort();
+       }
+       if (!buxton_array_add(array, label_copy)) {
+               abort();
+       }
+       if (!buxton_array_add(array, full_key)) {
+               abort();
+       }
+
+       ret = hashmap_put(db, full_key, array);
+       if (ret != 1) {
+               if (ret == -ENOMEM) {
+                       abort();
+               }
+               /* remove the old value */
+               stored = (BuxtonArray *)hashmap_remove(db, full_key);
+               assert(stored);
+
+               /* free the data */
+               d = buxton_array_get(stored, 0);
+               data_free(d);
+               l = buxton_array_get(stored, 1);
+               string_free(l);
+               k = buxton_array_get(stored, 2);
+               free(k);
+               buxton_array_free(&stored, NULL);
+               ret = hashmap_put(db, full_key, array);
+               if (ret != 1) {
+                       abort();
+               }
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
+                     BuxtonString *label)
+{
+       Hashmap *db;
+       BuxtonArray *stored;
+       BuxtonData *d;
+       BuxtonString *l;
+       char *full_key = NULL;
+       int ret;
+
+       assert(layer);
+       assert(key);
+       assert(label);
+       assert(data);
+
+       db = _db_for_resource(layer);
+       if (!db) {
+               /*
+                * Set negative here to indicate layer not found
+                * rather than key not found, optimization for
+                * set value
+                */
+               ret = -ENOENT;
+               goto end;
+       }
+
+       if (key->name.value) {
+               if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) {
+                       abort();
+               }
+       } else {
+               full_key = strdup(key->group.value);
+               if (!full_key) {
+                       abort();
+               }
+       }
+
+       stored = (BuxtonArray *)hashmap_get(db, full_key);
+       if (!stored) {
+               ret = ENOENT;
+               goto end;
+       }
+       d = buxton_array_get(stored, 0);
+       if (d->type != key->type) {
+               ret = EINVAL;
+               goto end;
+       }
+
+       if (!buxton_data_copy(d, data)) {
+               abort();
+       }
+
+       l = buxton_array_get(stored, 1);
+       if (!buxton_string_copy(l, label)) {
+               abort();
+       }
+
+       ret = 0;
+
+end:
+       free(full_key);
+       return ret;
+}
+
+static int unset_value(BuxtonLayer *layer,
+                       _BuxtonKey *key,
+                       __attribute__((unused)) BuxtonData *data,
+                       __attribute__((unused)) BuxtonString *label)
+{
+       Hashmap *db;
+       BuxtonArray *stored;
+       BuxtonData *d;
+       BuxtonString *l;
+       char *full_key = NULL;
+       char *k;
+       int ret;
+
+       assert(layer);
+       assert(key);
+
+       db = _db_for_resource(layer);
+       if (!db) {
+               ret = ENOENT;
+               goto end;
+       }
+
+       if (key->name.value) {
+               if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) {
+                       abort();
+               }
+       } else {
+               full_key = strdup(key->group.value);
+               if (!full_key) {
+                       abort();
+               }
+       }
+
+       /* test if the value exists */
+       stored = (BuxtonArray *)hashmap_remove(db, full_key);
+       if (!stored) {
+               ret = ENOENT;
+               goto end;
+       }
+
+       /* free the data */
+       d = buxton_array_get(stored, 0);
+       data_free(d);
+       l = buxton_array_get(stored, 1);
+       string_free(l);
+       k = buxton_array_get(stored, 2);
+       free(k);
+       buxton_array_free(&stored, NULL);
+
+       ret = 0;
+
+end:
+       free(full_key);
+       return ret;
+}
+
+_bx_export_ void buxton_module_destroy(void)
+{
+       const char *key1, *key2;
+       Iterator iteratori, iteratoro;
+       Hashmap *map;
+       BuxtonArray *array;
+       BuxtonData *d;
+       BuxtonString *l;
+
+       /* free all hashmaps */
+       HASHMAP_FOREACH_KEY(map, key1, _resources, iteratoro) {
+               HASHMAP_FOREACH_KEY(array, key2, map, iteratori) {
+                       hashmap_remove(map, key2);
+                       d = buxton_array_get(array, 0);
+                       data_free(d);
+                       l = buxton_array_get(array, 1);
+                       string_free(l);
+                       buxton_array_free(&array, NULL);
+               }
+               hashmap_remove(_resources, key1);
+               hashmap_free(map);
+               free((void *)key1);
+               map = NULL;
+       }
+       hashmap_free(_resources);
+       _resources = NULL;
+}
+
+_bx_export_ bool buxton_module_init(BuxtonBackend *backend)
+{
+
+       assert(backend);
+
+       /* Point the struct methods back to our own */
+       backend->set_value = &set_value;
+       backend->get_value = &get_value;
+       backend->unset_value = &unset_value;
+       backend->list_keys = NULL;
+       backend->create_db = NULL;
+
+       _resources = hashmap_new(string_hash_func, string_compare_func);
+       if (!_resources) {
+               abort();
+       }
+       return true;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/include/buxton.h b/src/include/buxton.h
new file mode 100644 (file)
index 0000000..8cff5f7
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file buxton.h Buxton public header
+ *
+ * This is the public part of libbuxton
+ *
+ * \mainpage Buxton
+ * \link buxton.h Public API
+ * \endlink - API listing for libbuxton
+ * \copyright Copyright (C) 2013 Intel corporation
+ * \par License
+ * GNU Lesser General Public License 2.1
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#if (__GNUC__ >= 4)
+/* Export symbols */
+#    define _bx_export_ __attribute__ ((visibility("default")))
+#else
+#  define _bx_export_
+#endif
+
+/**
+ * Possible data types for use in Buxton
+ */
+typedef enum BuxtonDataType {
+       BUXTON_TYPE_MIN,
+       STRING, /**<Represents type of a string value */
+       INT32, /**<Represents type of an int32_t value */
+       UINT32, /**<Represents type of an uint32_t value */
+       INT64, /**<Represents type of a int64_t value */
+       UINT64, /**<Represents type of a uint64_t value */
+       FLOAT, /**<Represents type of a float value */
+       DOUBLE, /**<Represents type of a double value */
+       BOOLEAN, /**<Represents type of a boolean value */
+       BUXTON_TYPE_MAX
+} BuxtonDataType;
+
+/**
+ * Buxton message types
+ */
+typedef enum BuxtonControlMessage {
+       BUXTON_CONTROL_MIN,
+       BUXTON_CONTROL_SET, /**<Set a value within Buxton */
+       BUXTON_CONTROL_SET_LABEL, /**<Set a label within Buxton */
+       BUXTON_CONTROL_CREATE_GROUP, /**<Create a group within Buxton */
+       BUXTON_CONTROL_REMOVE_GROUP, /**<Remove a group within Buxton */
+       BUXTON_CONTROL_GET, /**<Retrieve a value from Buxton */
+       BUXTON_CONTROL_UNSET, /**<Unset a value within Buxton */
+       BUXTON_CONTROL_LIST, /**<List keys within a Buxton layer */
+       BUXTON_CONTROL_STATUS, /**<Status code follows */
+       BUXTON_CONTROL_NOTIFY, /**<Register for notification */
+       BUXTON_CONTROL_UNNOTIFY, /**<Opt out of notifications */
+       BUXTON_CONTROL_CHANGED, /**<A key changed in Buxton */
+       BUXTON_CONTROL_MAX
+} BuxtonControlMessage;
+
+/**
+ * Used to communicate with Buxton
+ */
+typedef struct BuxtonClient *BuxtonClient;
+
+/**
+ * Represents a data key in Buxton
+ */
+typedef struct BuxtonKey *BuxtonKey;
+
+/**
+ * Represents daemon's reply to client
+ */
+typedef struct BuxtonResponse *BuxtonResponse;
+
+/**
+ * Prototype for callback functions
+ *
+ * Takes a BuxtonResponse and returns void.
+ */
+typedef void (*BuxtonCallback)(BuxtonResponse, void *);
+
+/* Buxton API Methods */
+
+/**
+ * Sets path to buxton configuration file
+ * @param path Path to the buxton configuration file to use
+ * @return An int with 0 indicating success or an errno value
+ */
+_bx_export_ int buxton_set_conf_file(char *path);
+
+/**
+ * Open a connection to Buxton
+ * @param client A BuxtonClient pointer
+ * @return A boolean value, indicating success of the operation
+ */
+_bx_export_ int buxton_open(BuxtonClient *client)
+       __attribute__((warn_unused_result));
+
+/**
+ * Close the connection to Buxton
+ * @param client A BuxtonClient
+ */
+_bx_export_ void buxton_close(BuxtonClient client);
+
+/**
+ * Set a value within Buxton
+ * @param client An open client connection
+ * @param layer The layer to manipulate
+ * @param key The key to set
+ * @param value A pointer to a supported data type
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return A int value, indicating success of the operation
+ */
+_bx_export_ int buxton_set_value(BuxtonClient client,
+                                BuxtonKey key,
+                                void *value,
+                                BuxtonCallback callback,
+                                void *data,
+                                bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Set a label within Buxton
+ *
+ * @note This is a privileged operation; the return value will be false for unprivileged clients.
+ *
+ * @param client An open client connection
+ * @param layer The layer to manipulate
+ * @param key The key or group name
+ * @param value A struct containing the label to set
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_set_label(BuxtonClient client,
+                                BuxtonKey key,
+                                char *value,
+                                BuxtonCallback callback,
+                                void *data,
+                                bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Create a group within Buxton
+ *
+ * @param client An open client connection
+ * @param key A BuxtonKey with only layer and group names initialized
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_create_group(BuxtonClient client,
+                                   BuxtonKey key,
+                                   BuxtonCallback callback,
+                                   void *data,
+                                   bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Remove a group within Buxton
+ *
+ * @param client An open client connection
+ * @param key A BuxtonKey with only layer and group names initialized
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_remove_group(BuxtonClient client,
+                                   BuxtonKey key,
+                                   BuxtonCallback callback,
+                                   void *data,
+                                   bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Retrieve a value from Buxton
+ * @param client An open client connection
+ * @param key The key to retrieve
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_get_value(BuxtonClient client,
+                                BuxtonKey key,
+                                BuxtonCallback callback,
+                                void *data,
+                                bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * List all keys within a given layer in Buxon
+ * @param client An open client connection
+ * @param layer_name The layer to query
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An boolean value, indicating success of the operation
+ */
+_bx_export_ int buxton_client_list_keys(BuxtonClient client,
+                                       char *layer_name,
+                                       BuxtonCallback callback,
+                                       void *data,
+                                       bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Register for notifications on the given key in all layers
+ * @param client An open client connection
+ * @param key The key to register interest with
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_register_notification(BuxtonClient client,
+                                            BuxtonKey key,
+                                            BuxtonCallback callback,
+                                            void *data,
+                                            bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Unregister from notifications on the given key in all layers
+ * @param client An open client connection
+ * @param key The key to remove registered interest from
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_unregister_notification(BuxtonClient client,
+                                              BuxtonKey key,
+                                              BuxtonCallback callback,
+                                              void *data,
+                                              bool sync)
+       __attribute__((warn_unused_result));
+
+
+/**
+ * Unset a value by key in the given BuxtonLayer
+ * @param client An open client connection
+ * @param key The key to remove
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @param sync Indicator for running a synchronous request
+ * @return An int value, indicating success of the operation
+ */
+_bx_export_ int buxton_unset_value(BuxtonClient client,
+                                  BuxtonKey key,
+                                  BuxtonCallback callback,
+                                  void *data,
+                                  bool sync)
+       __attribute__((warn_unused_result));
+
+/**
+ * Process messages on the socket
+ * @note Will not block, useful after poll in client application
+ * @param client An open client connection
+ * @return Number of messages processed or -1 if there was an error
+ */
+_bx_export_ ssize_t buxton_client_handle_response(BuxtonClient client)
+       __attribute__((warn_unused_result));
+
+/**
+ * Create a key for item lookup in buxton
+ * @param group Pointer to a character string representing a group
+ * @param name Pointer to a character string representing a name
+ * @param layer Pointer to a character string representing a layer (optional)
+ * @return A pointer to a BuxtonString containing the key
+ */
+_bx_export_ BuxtonKey buxton_key_create(char *group, char *name, char *layer,
+                                     BuxtonDataType type)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the group portion of a buxton key
+ * @param key A BuxtonKey
+ * @return A pointer to a character string containing the key's group
+ */
+_bx_export_ char *buxton_key_get_group(BuxtonKey key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the name portion of a buxton key
+ * @param key a BuxtonKey
+ * @return A pointer to a character string containing the key's name
+ */
+_bx_export_ char *buxton_key_get_name(BuxtonKey key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the layer portion of a buxton key
+ * @param key a BuxtonKey
+ * @return A pointer to a character string containing the key's layer
+ */
+_bx_export_ char *buxton_key_get_layer(BuxtonKey key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the type portion of a buxton key
+ * @param key a BuxtonKey
+ * @return The BuxtonDataType of a key
+ */
+_bx_export_ BuxtonDataType buxton_key_get_type(BuxtonKey key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Free BuxtonKey
+ * @param key a BuxtonKey
+ */
+_bx_export_ void buxton_key_free(BuxtonKey key);
+
+/**
+ * Get the type of a buxton response
+ * @param response a BuxtonResponse
+ * @return BuxtonControlMessage enum indicating the type of the response
+ */
+_bx_export_ BuxtonControlMessage buxton_response_type(BuxtonResponse response)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the status of a buxton response
+ * @param response a BuxtonResponse
+ * @return int32_t enum indicating the status of the response
+ */
+_bx_export_ int32_t buxton_response_status(BuxtonResponse response)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the key for a buxton response
+ * @param response a BuxtonResponse
+ * @return BuxtonKey from the response
+ */
+_bx_export_ BuxtonKey buxton_response_key(BuxtonResponse response)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the value for a buxton response
+ * @param response a BuxtonResponse
+ * @return pointer to data from the response
+ */
+_bx_export_ void *buxton_response_value(BuxtonResponse response)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/libbuxton/lbuxton.c b/src/libbuxton/lbuxton.c
new file mode 100644 (file)
index 0000000..0effefe
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file lbuxton.c Buxton library implementation
+ */
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "buxton.h"
+#include "buxtonclient.h"
+#include "buxtonkey.h"
+#include "buxtonresponse.h"
+#include "buxtonstring.h"
+#include "configurator.h"
+#include "hashmap.h"
+#include "log.h"
+#include "protocol.h"
+#include "util.h"
+
+static Hashmap *key_hash = NULL;
+
+int buxton_set_conf_file(char *path)
+{
+       int r;
+       struct stat st;
+
+       r = stat(path, &st);
+       if (r == -1) {
+               return errno;
+       } else {
+               if (st.st_mode & S_IFDIR) {
+                       return EINVAL;
+               }
+       }
+
+       buxton_add_cmd_line(CONFIG_CONF_FILE, path);
+
+       return 0;
+}
+
+int buxton_open(BuxtonClient *client)
+{
+       _BuxtonClient **c = (_BuxtonClient **)client;
+       _BuxtonClient *cl = NULL;
+       int bx_socket, r;
+       struct sockaddr_un remote;
+       size_t sock_name_len;
+
+       if ((bx_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+               return -1;
+       }
+
+       remote.sun_family = AF_UNIX;
+       sock_name_len = strlen(buxton_socket()) + 1;
+       if (sock_name_len >= sizeof(remote.sun_path)) {
+               buxton_log("Provided socket name: %s is too long, maximum allowed length is %d bytes\n",
+                          buxton_socket(), sizeof(remote.sun_path));
+               return -1;
+       }
+
+       strncpy(remote.sun_path, buxton_socket(), sock_name_len);
+       r = connect(bx_socket, (struct sockaddr *)&remote, sizeof(remote));
+       if ( r == -1) {
+               close(bx_socket);
+               return -1;
+       }
+
+       if (fcntl(bx_socket, F_SETFL, O_NONBLOCK)) {
+               close(bx_socket);
+               return -1;
+       }
+
+       if (!setup_callbacks()) {
+               close(bx_socket);
+               return -1;
+       }
+
+       cl = malloc0(sizeof(_BuxtonClient));
+       if (!cl) {
+               close(bx_socket);
+               return -1;
+       }
+
+       cl->fd = bx_socket;
+       *c = cl;
+
+       return bx_socket;
+}
+
+void buxton_close(BuxtonClient client)
+{
+       _BuxtonClient *c;
+       BuxtonKey key = NULL;
+       Iterator i;
+
+       /* Free all remaining allocated keys */
+       HASHMAP_FOREACH_KEY(key, key, key_hash, i) {
+               hashmap_remove_value(key_hash, key, key);
+               buxton_key_free(key);
+       }
+
+       hashmap_free(key_hash);
+
+       if (!client) {
+               return;
+       }
+
+       c = (_BuxtonClient *)client;
+
+       cleanup_callbacks();
+       close(c->fd);
+       c->direct = 0;
+       c->fd = -1;
+       free(c);
+}
+
+int buxton_get_value(BuxtonClient client,
+                    BuxtonKey key,
+                    BuxtonCallback callback,
+                    void *data,
+                    bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !(k->group.value) || !(k->name.value) ||
+           k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) {
+               return EINVAL;
+       }
+
+       r = buxton_wire_get_value((_BuxtonClient *)client, k, callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_register_notification(BuxtonClient client,
+                                BuxtonKey key,
+                                BuxtonCallback callback,
+                                void *data,
+                                bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !k->group.value || !k->name.value ||
+           k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) {
+               return EINVAL;
+       }
+
+       r = buxton_wire_register_notification((_BuxtonClient *)client, k,
+                                             callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_unregister_notification(BuxtonClient client,
+                                  BuxtonKey key,
+                                  BuxtonCallback callback,
+                                  void *data,
+                                  bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !k->group.value || !k->name.value ||
+           k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) {
+               return EINVAL;
+       }
+
+       r = buxton_wire_unregister_notification((_BuxtonClient *)client, k,
+                                               callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_set_value(BuxtonClient client,
+                    BuxtonKey key,
+                    void *value,
+                    BuxtonCallback callback,
+                    void *data,
+                    bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !k->group.value || !k->name.value || !k->layer.value ||
+           k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || !value) {
+               return EINVAL;
+       }
+
+       r = buxton_wire_set_value((_BuxtonClient *)client, k, value, callback,
+                                 data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_set_label(BuxtonClient client,
+                    BuxtonKey key,
+                    char *value,
+                    BuxtonCallback callback,
+                    void *data,
+                    bool sync)
+{
+       bool r;
+       int ret = 0;
+       BuxtonString v;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !k->group.value || !k->layer.value || !value) {
+               return EINVAL;
+       }
+
+       k->type = STRING;
+       v = buxton_string_pack(value);
+
+       r = buxton_wire_set_label((_BuxtonClient *)client, k, &v, callback,
+                                 data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_create_group(BuxtonClient client,
+                       BuxtonKey key,
+                       BuxtonCallback callback,
+                       void *data,
+                       bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       /* We require the key name to be NULL, since it is not used for groups */
+       if (!k || !k->group.value || k->name.value || !k->layer.value) {
+               return EINVAL;
+       }
+
+       k->type = STRING;
+       r = buxton_wire_create_group((_BuxtonClient *)client, k, callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_remove_group(BuxtonClient client,
+                       BuxtonKey key,
+                       BuxtonCallback callback,
+                       void *data,
+                       bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       /* We require the key name to be NULL, since it is not used for groups */
+       if (!k || !k->group.value || k->name.value || !k->layer.value) {
+               return EINVAL;
+       }
+
+       k->type = STRING;
+       r = buxton_wire_remove_group((_BuxtonClient *)client, k, callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_client_list_keys(BuxtonClient client,
+                           char *layer_name,
+                           BuxtonCallback callback,
+                           void *data,
+                           bool sync)
+{
+       bool r;
+       int ret = 0;
+       BuxtonString l;
+
+       if (!layer_name) {
+               return EINVAL;
+       }
+
+       l = buxton_string_pack(layer_name);
+
+       r = buxton_wire_list_keys((_BuxtonClient *)client, &l, callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int buxton_unset_value(BuxtonClient client,
+                      BuxtonKey key,
+                      BuxtonCallback callback,
+                      void *data,
+                      bool sync)
+{
+       bool r;
+       int ret = 0;
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k || !k->group.value || !k->name.value || !k->layer.value ||
+           k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) {
+               return EINVAL;
+       }
+
+       r = buxton_wire_unset_value((_BuxtonClient *)client, k, callback, data);
+       if (!r) {
+               return -1;
+       }
+
+       if (sync) {
+               ret = buxton_wire_get_response(client);
+               if (ret <= 0) {
+                       ret = -1;
+               } else {
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+BuxtonKey buxton_key_create(char *group, char *name, char *layer,
+                         BuxtonDataType type)
+{
+       _BuxtonKey *key = NULL;
+       char *g = NULL;
+       char *n = NULL;
+       char *l = NULL;
+
+       if (!group) {
+               goto fail;
+       }
+
+       if (type <= BUXTON_TYPE_MIN || type >= BUXTON_TYPE_MAX) {
+               goto fail;
+       }
+
+       if (!key_hash) {
+               /* Create on hashmap on first call to key_create */
+               key_hash = hashmap_new(trivial_hash_func, trivial_compare_func);
+               if (!key_hash) {
+                       return NULL;
+               }
+       }
+
+       g = strdup(group);
+       if (!g) {
+               goto fail;
+       }
+
+       if (name) {
+               n = strdup(name);
+               if (!n) {
+                       goto fail;
+               }
+       }
+
+       if (layer) {
+               l = strdup(layer);
+               if (!l) {
+                       goto fail;
+               }
+       }
+
+       key = malloc0(sizeof(_BuxtonKey));
+       if (!key) {
+               goto fail;
+       }
+
+       key->group.value = g;
+       key->group.length = (uint32_t)strlen(g) + 1;
+       if (name) {
+               key->name.value = n;
+               key->name.length = (uint32_t)strlen(n) + 1;
+       } else {
+               key->name.value = NULL;
+               key->name.length = 0;
+       }
+       if (layer) {
+               key->layer.value = l;
+               key->layer.length = (uint32_t)strlen(l) + 1;
+       } else {
+               key->layer.value = NULL;
+               key->layer.length = 0;
+       }
+       key->type = type;
+
+       /* Add new keys to internal hash for cleanup on close */
+       hashmap_put(key_hash, key, key);
+
+       return (BuxtonKey)key;
+
+fail:
+       free(g);
+       free(n);
+       free(l);
+       return NULL;
+}
+
+char *buxton_key_get_group(BuxtonKey key)
+{
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!key) {
+               return NULL;
+       }
+
+       return get_group(k);
+}
+
+char *buxton_key_get_name(BuxtonKey key)
+{
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!key) {
+               return NULL;
+       }
+
+       return get_name(k);
+}
+
+char *buxton_key_get_layer(BuxtonKey key)
+{
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!key) {
+               return NULL;
+       }
+
+       return get_layer(k);
+}
+
+BuxtonDataType buxton_key_get_type(BuxtonKey key)
+{
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!key) {
+               return -1;
+       }
+
+       return k->type;
+}
+
+void buxton_key_free(BuxtonKey key)
+{
+       _BuxtonKey *k = (_BuxtonKey *)key;
+
+       if (!k) {
+               return;
+       }
+
+       hashmap_remove_value(key_hash, key, key);
+
+       free(k->group.value);
+       free(k->name.value);
+       free(k->layer.value);
+       free(k);
+}
+
+ssize_t buxton_client_handle_response(BuxtonClient client)
+{
+       return buxton_wire_handle_response((_BuxtonClient *)client);
+}
+
+BuxtonControlMessage buxton_response_type(BuxtonResponse response)
+{
+       _BuxtonResponse *r = (_BuxtonResponse *)response;
+
+       if (!response) {
+               return -1;
+       }
+
+       return r->type;
+}
+
+int32_t buxton_response_status(BuxtonResponse response)
+{
+       BuxtonData *d;
+       _BuxtonResponse *r = (_BuxtonResponse *)response;
+
+       if (!response) {
+               return -1;
+       }
+
+       if (buxton_response_type(response) == BUXTON_CONTROL_CHANGED) {
+               return 0;
+       }
+
+       d = buxton_array_get(r->data, 0);
+
+       if (d) {
+               return d->store.d_int32;
+       }
+
+       return -1;
+}
+
+BuxtonKey buxton_response_key(BuxtonResponse response)
+{
+       _BuxtonKey *key = NULL;
+       _BuxtonResponse *r = (_BuxtonResponse *)response;
+
+       if (!response) {
+               return NULL;
+       }
+
+       if (buxton_response_type(response) == BUXTON_CONTROL_LIST) {
+               return NULL;
+       }
+
+       key = malloc0(sizeof(_BuxtonKey));
+       if (!key) {
+               return NULL;
+       }
+
+       if (!buxton_key_copy(r->key, key)) {
+               free(key);
+               return NULL;
+       }
+
+       return (BuxtonKey)key;
+}
+
+void *buxton_response_value(BuxtonResponse response)
+{
+       void *p = NULL;
+       BuxtonData *d = NULL;
+       _BuxtonResponse *r = (_BuxtonResponse *)response;
+       BuxtonControlMessage type;
+
+       if (!response) {
+               return NULL;
+       }
+
+       type = buxton_response_type(response);
+       if (type == BUXTON_CONTROL_GET) {
+               d = buxton_array_get(r->data, 1);
+       } else if (type == BUXTON_CONTROL_CHANGED) {
+               if (r->data->len) {
+                       d = buxton_array_get(r->data, 0);
+               }
+       } else {
+               goto out;
+       }
+
+       if (!d) {
+               goto out;
+       }
+
+       switch (d->type) {
+       case STRING:
+               return strdup(d->store.d_string.value);
+       case INT32:
+               p = malloc0(sizeof(int32_t));
+               if (!p) {
+                       goto out;
+               }
+               *(int32_t *)p = (int32_t)d->store.d_int32;
+               break;
+       case UINT32:
+               p = malloc0(sizeof(uint32_t));
+               if (!p) {
+                       goto out;
+               }
+               *(uint32_t *)p = (uint32_t)d->store.d_uint32;
+               break;
+       case INT64:
+               p = malloc0(sizeof(int64_t));
+               if (!p) {
+                       goto out;
+               }
+               *(int64_t *)p = (int64_t)d->store.d_int64;
+               break;
+       case UINT64:
+               p = malloc0(sizeof(uint64_t));
+               if (!p) {
+                       goto out;
+               }
+               *(uint64_t *)p = (uint64_t)d->store.d_uint64;
+               break;
+       case FLOAT:
+               p = malloc0(sizeof(float));
+               if (!p) {
+                       goto out;
+               }
+               *(float *)p = (float)d->store.d_float;
+               break;
+       case DOUBLE:
+               p = malloc0(sizeof(double));
+               if (!p) {
+                       goto out;
+               }
+               *(double *)p = (double)d->store.d_double;
+               break;
+       case BOOLEAN:
+               p = malloc0(sizeof(bool));
+               if (!p) {
+                       goto out;
+               }
+               *(bool *)p = (bool)d->store.d_boolean;
+               break;
+       default:
+               break;
+       }
+
+out:
+       return p;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/libbuxton/lbuxton.sym b/src/libbuxton/lbuxton.sym
new file mode 100644 (file)
index 0000000..37c36d0
--- /dev/null
@@ -0,0 +1,27 @@
+BUXTON_1 {
+       global:
+               buxton_set_conf_file;
+               buxton_open;
+               buxton_close;
+               buxton_set_value;
+               buxton_set_label;
+               buxton_create_group;
+               buxton_remove_group;
+               buxton_get_value;
+               buxton_unset_value;
+               buxton_register_notification;
+               buxton_unregister_notification;
+               buxton_client_handle_response;
+               buxton_key_get_group;
+               buxton_key_get_name;
+               buxton_key_get_layer;
+               buxton_key_get_type;
+               buxton_key_create;
+               buxton_key_free;
+               buxton_response_status;
+               buxton_response_type;
+               buxton_response_key;
+               buxton_response_value;
+       local:
+               *;
+};
diff --git a/src/security/smack.c b/src/security/smack.c
new file mode 100644 (file)
index 0000000..2f85b3f
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/inotify.h>
+
+#include "buxton.h"
+#include "buxtonkey.h"
+#include "configurator.h"
+#include "direct.h"
+#include "hashmap.h"
+#include "log.h"
+#include "smack.h"
+#include "util.h"
+
+static Hashmap *_smackrules = NULL;
+/* set to true unless Smack support is not detected by the daemon */
+static bool have_smack = true;
+
+#define smack_check() do { if (!have_smack) { return true; } } while (0);
+
+
+bool buxton_smack_enabled(void)
+{
+       return have_smack;
+}
+
+bool buxton_cache_smack_rules(void)
+{
+       smack_check();
+
+       FILE *load_file = NULL;
+       char *rule_pair = NULL;
+       int ret = true;
+       bool have_rules = false;
+       struct stat buf;
+
+       if (_smackrules) {
+               hashmap_free(_smackrules);
+       }
+
+       _smackrules = hashmap_new(string_hash_func, string_compare_func);
+
+       if (!_smackrules) {
+               abort();
+       }
+
+       /* FIXME: should check for a proper mount point instead */
+       if ((stat(SMACK_MOUNT_DIR, &buf) == -1) || !S_ISDIR(buf.st_mode)) {
+               buxton_log("Smack filesystem not detected; disabling Smack checks\n");
+               have_smack = false;
+               goto end;
+       }
+
+       load_file = fopen(buxton_smack_load_file(), "r");
+
+       if (!load_file) {
+               switch (errno) {
+               case ENOENT:
+                       buxton_log("Smackfs load2 file not found; disabling Smack checks\n");
+                       have_smack = false;
+                       goto end;
+               default:
+                       buxton_log("fopen(): %m\n");
+                       ret = false;
+                       goto end;
+               }
+       }
+
+       do {
+               int r;
+               int chars;
+               BuxtonKeyAccessType *accesstype;
+
+               char subject[SMACK_LABEL_LEN+1] = { 0, };
+               char object[SMACK_LABEL_LEN+1] = { 0, };
+               char access[ACC_LEN] = { 0, };
+
+               /* read contents from load2 */
+               chars = fscanf(load_file, "%s %s %s\n", subject, object, access);
+
+               if (ferror(load_file)) {
+                       buxton_log("fscanf(): %m\n");
+                       ret = false;
+                       goto end;
+               }
+
+               if (!have_rules && chars == EOF && feof(load_file)) {
+                       buxton_debug("No loaded Smack rules found\n");
+                       goto end;
+               }
+
+               if (chars != 3) {
+                       buxton_log("Corrupt load file detected\n");
+                       ret = false;
+                       goto end;
+               }
+
+               have_rules = true;
+
+               r = asprintf(&rule_pair, "%s %s", subject, object);
+               if (r == -1) {
+                       abort();
+               }
+
+               accesstype = malloc0(sizeof(BuxtonKeyAccessType));
+               if (!accesstype) {
+                       abort();
+               }
+
+               *accesstype = ACCESS_NONE;
+
+               if (strchr(access, 'r')) {
+                       *accesstype |= ACCESS_READ;
+               }
+
+               if (strchr(access, 'w')) {
+                       *accesstype |= ACCESS_WRITE;
+               }
+
+               hashmap_put(_smackrules, rule_pair, accesstype);
+
+       } while (!feof(load_file));
+
+end:
+       if (load_file) {
+               fclose(load_file);
+       }
+
+       return ret;
+}
+
+bool buxton_check_smack_access(BuxtonString *subject, BuxtonString *object, BuxtonKeyAccessType request)
+{
+       smack_check();
+
+       _cleanup_free_ char *key = NULL;
+       int r;
+       BuxtonKeyAccessType *access;
+
+       assert(subject);
+       assert(object);
+       assert((request == ACCESS_READ) || (request == ACCESS_WRITE));
+       assert(_smackrules);
+
+       buxton_debug("Subject: %s\n", subject->value);
+       buxton_debug("Object: %s\n", object->value);
+
+       /* check the builtin Smack rules first */
+       if (streq(subject->value, "*")) {
+               return false;
+       }
+
+       if (streq(object->value, "@") || streq(subject->value, "@")) {
+               return true;
+       }
+
+       if (streq(object->value, "*")) {
+               return true;
+       }
+
+       if (streq(subject->value, object->value)) {
+               return true;
+       }
+
+       if (request == ACCESS_READ) {
+               if (streq(object->value, "_")) {
+                       return true;
+               }
+               if (streq(subject->value, "^")) {
+                       return true;
+               }
+       }
+
+       /* finally, check the loaded rules */
+       r = asprintf(&key, "%s %s", subject->value, object->value);
+       if (r == -1) {
+               abort();
+       }
+
+       buxton_debug("Key: %s\n", key);
+
+       access = hashmap_get(_smackrules, key);
+       if (!access) {
+               /* A null value is not an error, since clients may try to
+                * read/write keys with labels that are not in the loaded
+                * rule set. In this situation, access is simply denied,
+                * because there are no further rules to consider.
+                */
+               buxton_debug("Value of key '%s' is NULL\n", key);
+               return false;
+       }
+
+       /* After debugging, change this code to: */
+       /* return ((*access) & request); */
+       if (access) {
+               buxton_debug("Value: %x\n", *access);
+       }
+
+       if (request == ACCESS_READ && (*access) & request) {
+               buxton_debug("Read access granted!\n");
+               return true;
+       }
+
+       if (request == ACCESS_WRITE && ((*access) & ACCESS_READ && (*access) & ACCESS_WRITE)) {
+               buxton_debug("Write access granted!\n");
+               return true;
+       }
+
+       buxton_debug("Access denied!\n");
+       return false;
+}
+
+int buxton_watch_smack_rules(void)
+{
+       if (!have_smack) {
+               errno = 0;
+               return -1;
+       }
+
+       int fd;
+
+       fd = inotify_init1(IN_NONBLOCK);
+       if (fd < 0) {
+               buxton_log("inotify_init(): %m\n");
+               return -1;
+       }
+       if (inotify_add_watch(fd, buxton_smack_load_file(), IN_CLOSE_WRITE) < 0) {
+               buxton_log("inotify_add_watch(): %m\n");
+               return -1;
+       }
+       return fd;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/security/smack.h b/src/security/smack.h
new file mode 100644 (file)
index 0000000..c83d06a
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "backend.h"
+#include "buxton.h"
+
+/**
+ * Maximum length for a Smack label
+ */
+#define SMACK_LABEL_LEN 255
+
+/**
+ * Smack label xattr key
+ */
+#define SMACK_ATTR_NAME "security.SMACK64"
+
+/**
+ * Smackfs mount directory
+ */
+#define SMACK_MOUNT_DIR "/sys/fs/smackfs"
+
+/**
+ * Maximum length of a Smack rule access string
+ */
+#define ACC_LEN 5
+
+/**
+ * Represents client access to a given resource
+ */
+typedef enum BuxtonKeyAccessType {
+       ACCESS_NONE = 0, /**<No access permitted */
+       ACCESS_READ = 1 << 0, /**<Read access permitted */
+       ACCESS_WRITE = 1 << 1, /**<Write access permitted */
+       ACCESS_MAXACCESSTYPES = 1 << 2
+} BuxtonKeyAccessType;
+
+/**
+ * Check whether Smack is enabled in buxtond
+ * @return a boolean value, indicating whether Smack is enabled
+ */
+bool buxton_smack_enabled(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * Load Smack rules from the kernel
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_cache_smack_rules(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * Check whether the smack access matches the buxton client access
+ * @param subject Smack subject label
+ * @param object Smack object label
+ * @param request The buxton access type being queried
+ * @return true if the smack access matches the given request, otherwise false
+ */
+bool buxton_check_smack_access(BuxtonString *subject,
+                              BuxtonString *object,
+                              BuxtonKeyAccessType request)
+       __attribute__((warn_unused_result));
+
+/**
+ * Set up inotify to track Smack rule file for changes
+ * @return an exit code for the operation
+ */
+int buxton_watch_smack_rules(void)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/backend.c b/src/shared/backend.c
new file mode 100644 (file)
index 0000000..29eb8f6
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <dlfcn.h>
+
+#include "configurator.h"
+#include "backend.h"
+#include "hashmap.h"
+#include "util.h"
+#include "log.h"
+#include "buxtonarray.h"
+#include "smack.h"
+
+/**
+ * Create a BuxtonLayer out of a ConfigLayer
+ *
+ * Validates the data from the config file and creates BuxtonLayer.
+ *
+ * @param conf_layer the ConfigLayer to validate
+ *
+ * @return a new BuxtonLayer.  Callers are responsible for managing
+ * this memory
+ */
+static BuxtonLayer *buxton_layer_new(ConfigLayer *conf_layer);
+
+/* Load layer configurations from disk */
+void buxton_init_layers(BuxtonConfig *config)
+{
+       Hashmap *layers = NULL;
+       int nlayers = 0;
+       ConfigLayer *config_layers = NULL;
+       int r;
+
+       nlayers = buxton_key_get_layers(&config_layers);
+       layers = hashmap_new(string_hash_func, string_compare_func);
+       if (!layers) {
+               abort();
+       }
+
+       for (int n = 0; n < nlayers; n++) {
+               BuxtonLayer *layer;
+
+               layer = buxton_layer_new(&(config_layers[n]));
+               if (!layer) {
+                       abort();
+               }
+
+               r = hashmap_put(layers, layer->name.value, layer);
+               if (r != 1) {
+                       abort();
+               }
+       }
+
+       config->layers = layers;
+       free(config_layers);
+}
+
+static bool is_read_only(ConfigLayer *conf_layer)
+{
+       return strcmp(conf_layer->access, "read-only") == 0;
+}
+
+static BuxtonLayer *buxton_layer_new(ConfigLayer *conf_layer)
+{
+       BuxtonLayer *out;
+
+       assert(conf_layer);
+       out= malloc0(sizeof(BuxtonLayer));
+       if (!out) {
+               abort();
+       }
+
+       if (conf_layer->priority < 0) {
+               goto fail;
+       }
+       out->name.value = strdup(conf_layer->name);
+       if (!out->name.value) {
+               abort();
+       }
+       out->name.length = (uint32_t)strlen(conf_layer->name);
+
+       if (strcmp(conf_layer->type, "System") == 0) {
+               out->type = LAYER_SYSTEM;
+       } else if (strcmp(conf_layer->type, "User") == 0) {
+               out->type = LAYER_USER;
+       } else {
+               buxton_log("Layer %s has unknown type: %s\n", conf_layer->name, conf_layer->type);
+               goto fail;
+       }
+
+       if (strcmp(conf_layer->backend, "gdbm") == 0) {
+               out->backend = BACKEND_GDBM;
+       } else if (strcmp(conf_layer->backend, "memory") == 0) {
+               out->backend = BACKEND_MEMORY;
+       } else {
+               buxton_log("Layer %s has unknown database: %s\n", conf_layer->name, conf_layer->backend);
+               goto fail;
+       }
+
+       if (conf_layer->description != NULL) {
+               out->description = strdup(conf_layer->description);
+               if (!out->description) {
+                       abort();
+               }
+       }
+
+       out->readonly = is_read_only(conf_layer);
+       out->priority = conf_layer->priority;
+       return out;
+fail:
+       free(out->name.value);
+       free(out->description);
+       free(out);
+       return NULL;
+}
+
+static void init_backend(BuxtonConfig *config,
+                        BuxtonLayer *layer,
+                        BuxtonBackend **backend)
+{
+       void *handle, *cast;
+       _cleanup_free_ char *path = NULL;
+       const char *name;
+       char *error;
+       int r;
+       bool rb;
+       module_init_func i_func;
+       module_destroy_func d_func;
+       BuxtonBackend *backend_tmp;
+
+       assert(layer);
+       assert(backend);
+
+       if (layer->backend == BACKEND_GDBM) {
+               name = "gdbm";
+       } else if (layer->backend == BACKEND_MEMORY) {
+               name = "memory";
+       } else {
+               buxton_log("Invalid backend type for layer: %s\n", layer->name);
+               abort();
+       }
+
+       backend_tmp = hashmap_get(config->backends, name);
+
+       if (backend_tmp) {
+               *backend = backend_tmp;
+               return;
+       }
+
+       backend_tmp = malloc0(sizeof(BuxtonBackend));
+       if (!backend_tmp) {
+               abort();
+       }
+
+       r = asprintf(&path, "%s/%s.so", buxton_module_dir(), name);
+       if (r == -1) {
+               abort();
+       }
+
+       /* Load the module */
+       handle = dlopen(path, RTLD_LAZY);
+
+       if (!handle) {
+               buxton_log("dlopen(): %s\n", dlerror());
+               abort();
+       }
+
+       dlerror();
+       cast = dlsym(handle, "buxton_module_init");
+       if ((error = dlerror()) != NULL || !cast) {
+               buxton_log("dlsym(): %s\n", error);
+               abort();
+       }
+       memcpy(&i_func, &cast, sizeof(i_func));
+       dlerror();
+
+       cast = dlsym(handle, "buxton_module_destroy");
+       if ((error = dlerror()) != NULL || !cast) {
+               buxton_log("dlsym(): %s\n", error);
+               abort();
+       }
+       memcpy(&d_func, &cast, sizeof(d_func));
+
+       rb = i_func(backend_tmp);
+       if (!rb) {
+               buxton_log("buxton_module_init failed\n");
+               abort();
+       }
+
+       if (!config->backends) {
+               config->backends = hashmap_new(trivial_hash_func, trivial_compare_func);
+               if (!config->backends) {
+                       abort();
+               }
+       }
+
+       r = hashmap_put(config->backends, name, backend_tmp);
+       if (r != 1) {
+               abort();
+       }
+
+       backend_tmp->module = handle;
+       backend_tmp->destroy = d_func;
+
+       *backend = backend_tmp;
+}
+
+BuxtonBackend *backend_for_layer(BuxtonConfig *config,
+                                BuxtonLayer *layer)
+{
+       BuxtonBackend *backend;
+       int ret;
+
+       assert(layer);
+
+       if (!config->databases) {
+               config->databases = hashmap_new(string_hash_func, string_compare_func);
+               if (!config->databases) {
+                       abort();
+               }
+       }
+       if ((backend = (BuxtonBackend*)hashmap_get(config->databases, layer->name.value)) == NULL) {
+               /* attempt load of backend */
+               init_backend(config, layer, &backend);
+
+               ret = hashmap_put(config->databases, layer->name.value, backend);
+               if (ret != 1) {
+                       abort();
+               }
+       }
+       return (BuxtonBackend*)hashmap_get(config->databases, layer->name.value);
+}
+
+void destroy_backend(BuxtonBackend *backend)
+{
+
+       assert(backend);
+
+       backend->set_value = NULL;
+       backend->get_value = NULL;
+       backend->list_keys = NULL;
+       backend->unset_value = NULL;
+       backend->destroy();
+       dlclose(backend->module);
+       free(backend);
+       backend = NULL;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/backend.h b/src/shared/backend.h
new file mode 100644 (file)
index 0000000..edcde53
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file backend.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used by and for the backend
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <gdbm.h>
+
+#include "buxtonarray.h"
+#include "buxtondata.h"
+#include "buxtonstring.h"
+#include "protocol.h"
+#include "hashmap.h"
+
+/**
+ * Possible backends for Buxton
+ */
+typedef enum BuxtonBackendType {
+       BACKEND_UNSET = 0, /**<No backend set */
+       BACKEND_GDBM, /**<GDBM backend */
+       BACKEND_MEMORY, /**<Memory backend */
+       BACKEND_MAXTYPES
+} BuxtonBackendType;
+
+/**
+ * Buxton layer type
+ */
+typedef enum BuxtonLayerType {
+       LAYER_SYSTEM, /**<A system layer*/
+       LAYER_USER, /**<A user layer */
+       LAYER_MAXTYPES
+} BuxtonLayerType;
+
+/**
+ * Represents a layer within Buxton
+ *
+ * Keys can be stored in various layers within Buxton, using a variety
+ * of backend and configurations. This is all handled transparently and
+ * through a consistent API
+ */
+typedef struct BuxtonLayer {
+       BuxtonString name; /**<Name of the layer*/
+       BuxtonLayerType type; /**<Type of layer */
+       BuxtonBackendType backend; /**<Backend for this layer */
+       uid_t uid; /**<User ID for layers of type LAYER_USER */
+       int priority; /**<Priority of this layer */
+       char *description; /**<Description of this layer */
+       bool readonly; /**<Layer is readonly or not */
+} BuxtonLayer;
+
+/**
+ * Backend manipulation function
+ * @param layer The layer to manipulate or query
+ * @param key The key to manipulate or query
+ * @param data Set or get data, dependant on operation
+ * @param label The key's label
+ * @return a int value, indicating success of the operation or errno
+ */
+typedef int (*module_value_func) (BuxtonLayer *layer, _BuxtonKey *key,
+                                 BuxtonData *data, BuxtonString *label);
+
+/**
+ * Backend key list function
+ * @param layer The layer to query
+ * @param data Pointer to store BuxtonArray in
+ * @return a boolean value, indicating success of the operation
+ */
+typedef bool (*module_list_func) (BuxtonLayer *layer, BuxtonArray **data);
+
+/**
+ * Backend database creation function
+ * @param layer The layer matching the db to create
+ * @return A Database (not intended to be used from direct functions)
+ */
+typedef void *(*module_db_init_func) (BuxtonLayer *layer);
+
+/**
+ * Destroy (or shutdown) a backend module
+ */
+typedef void (*module_destroy_func) (void);
+
+/**
+ * A data-backend for Buxton
+ *
+ * Backends are controlled by Buxton for storing and retrieving data
+ */
+typedef struct BuxtonBackend {
+       void *module; /**<Private handle to the module */
+       module_destroy_func destroy; /**<Destroy method */
+       module_value_func set_value; /**<Set value function */
+       module_value_func get_value; /**<Get value function */
+       module_list_func list_keys; /**<List keys function */
+       module_value_func unset_value; /**<Unset value function */
+       module_db_init_func create_db; /**<DB file creation function */
+} BuxtonBackend;
+
+/**
+ * Stores internal configuration of Buxton
+ */
+typedef struct BuxtonConfig {
+       Hashmap *databases; /**<Database mapping */
+       Hashmap *layers; /**<Global layer configuration */
+       Hashmap *backends; /**<Backend mapping */
+} BuxtonConfig;
+
+/**
+ * Internal controller for Buxton
+ */
+typedef struct BuxtonControl {
+       _BuxtonClient client; /**<Valid client connection */
+       BuxtonConfig config; /**<Valid configuration (unused) */
+} BuxtonControl;
+
+/**
+ * Module initialisation function
+ * @param backend The backend to initialise
+ * @return A boolean value, representing the success of the operation
+ */
+typedef bool (*module_init_func) (BuxtonBackend *backend)
+       __attribute__((warn_unused_result));
+
+/**
+ * Return a valid backend for the given configuration and layer
+ * @param config A BuxtonControl's configuration
+ * @param layer The layer to query
+ * @return an initialised backend, or NULL if the layer is not found
+ */
+BuxtonBackend *backend_for_layer(BuxtonConfig *config,
+                                BuxtonLayer *layer)
+       __attribute__((warn_unused_result));
+
+/**
+ * Initialize layers using the configuration file
+ * @param config A BuxtonControl's configuration
+ */
+void buxton_init_layers(BuxtonConfig *config);
+
+/**
+ * Remove association with backend (free and dlclose)
+ * @param backend A BuxtonBackend to be removed
+ */
+void destroy_backend(BuxtonBackend *backend);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonarray.c b/src/shared/buxtonarray.c
new file mode 100644 (file)
index 0000000..c81b727
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxtonarray.h"
+#include "util.h"
+
+BuxtonArray *buxton_array_new(void)
+{
+       BuxtonArray *ret = NULL;
+       /* If this fails, we simply return NULL and allow the user
+        * to deal with the error */
+       ret = calloc(1, sizeof(BuxtonArray));
+       return ret;
+}
+
+bool buxton_array_add(BuxtonArray *array,
+                     void *data)
+{
+       uint new_len;
+       size_t curr, new_size;
+
+       if (!array || !data) {
+               return false;
+       }
+       if (!array->data) {
+               array->data = calloc(1, sizeof(void*));
+               if (!array->data) {
+                       return false;
+               }
+       }
+
+       new_len = array->len += 1;
+       curr = (size_t)(array->len*sizeof(void*));
+       new_size = curr + sizeof(void*);
+       if (new_len >= array->len) {
+               /* Resize the array to hold one more pointer */
+               array->data = greedy_realloc((void**)&array->data, &curr, new_size);
+               if (!array->data) {
+                       return false;
+               }
+       }
+       /* Store the pointer at the end of the array */
+       array->len = new_len;
+       array->data[array->len-1] = data;
+       return true;
+}
+
+void *buxton_array_get(BuxtonArray *array, uint16_t index)
+{
+       if (!array) {
+               return NULL;
+       }
+       if (index > array->len) {
+               return NULL;
+       }
+       return array->data[index];
+}
+
+
+void buxton_array_free(BuxtonArray **array,
+                      buxton_free_func free_method)
+{
+       int i;
+       if (!array || !*array) {
+               return;
+       }
+
+       if (free_method) {
+               /* Call the free_method on all members */
+               for (i = 0; i < (*array)->len; i++)
+                       free_method((*array)->data[i]);
+       }
+       /* Ensure this array is indeed gone. */
+       free((*array)->data);
+       free(*array);
+       *array = NULL;
+}
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonarray.h b/src/shared/buxtonarray.h
new file mode 100644 (file)
index 0000000..2137bb3
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "buxtondata.h"
+
+/**
+ * A dynamic array
+ * Represents daemon's reply to client
+ */
+typedef struct BuxtonArray {
+       void **data; /**<Dynamic array contents */
+       uint len; /**<Length of the array */
+} BuxtonArray;
+
+
+/**
+ * Valid function prototype for 'free' method
+ * @param p Pointer to free
+ */
+typedef void (*buxton_free_func) (void* p);
+
+/**
+ * Create a new BuxtonArray
+ * @returns BuxtonArray a newly allocated BuxtonArray
+ */
+BuxtonArray *buxton_array_new(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * Append data to BuxtonArray
+ * @param array Valid BuxtonArray
+ * @param data Pointer to add to this array
+ * @returns bool true if the data was added to the array
+ */
+bool buxton_array_add(BuxtonArray *array,
+                     void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Free an array, and optionally its members
+ * @param array valid BuxtonArray reference
+ * @param free_func Function to call to free all members, or NULL to leave allocated
+ */
+void buxton_array_free(BuxtonArray **array,
+                      buxton_free_func free_method);
+
+
+/**
+ * Retrieve a BuxtonData from the array by index
+ * @param array valid BuxtonArray reference
+ * @param index index of the element in the array
+ * @return a data pointer refered to by index, or NULL
+ */
+void *buxton_array_get(BuxtonArray *array, uint16_t index)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonclient.h b/src/shared/buxtonclient.h
new file mode 100644 (file)
index 0000000..a2394cf
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file client.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used for buxtonctl
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdbool.h>
+
+/**
+ * Used to communicate with Buxton
+ */
+typedef struct BuxtonClient {
+       int fd; /**<The file descriptor for the connection */
+       bool direct; /**<Only used for direction connections */
+       pid_t pid; /**<Process ID, used within libbuxton */
+       uid_t uid; /**<User ID of currently using user */
+} _BuxtonClient;
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtondata.h b/src/shared/buxtondata.h
new file mode 100644 (file)
index 0000000..6745ae6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include "buxton.h"
+#include "buxtonstring.h"
+
+/**
+ * Stores values in Buxton, may have only one value
+ */
+typedef union BuxtonDataStore {
+       BuxtonString d_string; /**<Stores a string value */
+       int32_t d_int32; /**<Stores an int32_t value */
+       uint32_t d_uint32; /**<Stores an uint32_t value */
+       int64_t d_int64; /**<Stores a int64_t value */
+       uint64_t d_uint64; /**<Stores a uint64_t value */
+       float d_float; /**<Stores a float value */
+       double d_double; /**<Stores a double value */
+       bool d_boolean; /**<Stores a boolean value */
+} BuxtonDataStore;
+
+/**
+ * Represents data in Buxton
+ *
+ * In Buxton we operate on all data using BuxtonData, for both set and
+ * get operations. The type must be set to the type of value being set
+ * in the BuxtonDataStore
+ */
+typedef struct BuxtonData {
+       BuxtonDataType type; /**<Type of data stored */
+       BuxtonDataStore store; /**<Contains one value, correlating to
+                              * type */
+} BuxtonData;
+
+static inline void buxton_string_to_data(BuxtonString *s, BuxtonData *d)
+{
+       d->type = STRING;
+       d->store.d_string.value = s->value;
+       d->store.d_string.length = s->length;
+}
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonkey.h b/src/shared/buxtonkey.h
new file mode 100644 (file)
index 0000000..3856060
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "buxton.h"
+#include "buxtonstring.h"
+
+/**
+ * Represents a data key in Buxton
+ */
+typedef struct BuxtonKey {
+       BuxtonString group; /**<Value of the key's group */
+       BuxtonString name; /**<Value of the key's name */
+       BuxtonString layer; /**<Value of the key's layer */
+       BuxtonDataType type; /**<Type of value associated with key */
+} _BuxtonKey;
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonlist.c b/src/shared/buxtonlist.c
new file mode 100644 (file)
index 0000000..6f9a589
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "buxtonlist.h"
+
+bool buxton_list_append(BuxtonList **list, void *data)
+{
+       BuxtonList *head = *list;
+
+       BuxtonList *parent = NULL;
+       BuxtonList *next = NULL;
+
+       if (!head) {
+               /* New head generation */
+               head = calloc(1, sizeof(BuxtonList));
+               if (!head) {
+                       return false;
+               }
+               next = parent = head;
+               head->size = 0;
+               next->tail = NULL;
+       }
+
+       if (!next) {
+               next = calloc(1, sizeof(BuxtonList));
+               if (!next) {
+                       return false;
+               }
+               if (head->tail) {
+                       parent = head->tail;
+               } else {
+                       parent = head;
+               }
+               parent->next = next;
+               head->tail = next;
+       }
+       head->size += 1;
+       next->data = data;
+       *list = head;
+       return true;
+}
+
+bool buxton_list_prepend(BuxtonList **list, void *data)
+{
+       BuxtonList *head = *list;
+       BuxtonList *prev = NULL;
+
+       if (!head) {
+               /* New head generation */
+               head = calloc(1, sizeof(BuxtonList));
+               if (!head) {
+                       return false;
+               }
+               prev = head;
+               head->size = 0;
+               prev->tail = head;
+               prev->next = NULL;
+       } else {
+               /* New item */
+               prev = calloc(1, sizeof(BuxtonList));
+               if (!prev) {
+                       return false;
+               }
+               prev->size = head->size+1;
+               head->size = 0;
+               prev->next = head;
+               prev->tail = head->tail;
+               head->tail = NULL;
+       }
+       /* Previous item is now the head */
+       prev->data = data;
+       *list = prev;
+
+       return true;
+}
+
+bool buxton_list_remove(BuxtonList **list, void *data, bool do_free)
+{
+       BuxtonList *head = *list;
+       BuxtonList *current = head;
+       BuxtonList *prev = head;
+
+       /* Determine the node inside the list */
+       while ((current != NULL) && (current->data != data)) {
+               prev = current;
+               current = current->next;
+       }
+       /* Not found */
+       if (!current) {
+               return false;
+       }
+
+       /* Data on the head (head removal)*/
+       if (current == head) {
+               if (head->next) {
+                       head->next->size = head->size -1;
+                       head->size = 0;
+               }
+               head = head->next;
+       } else {
+               /* Middle or end */
+               prev->next = current->next;
+               head->size--;
+       }
+
+       /* Update tail pointer */
+       if (head && head->tail == current) {
+               head->tail = prev;
+               head->tail->next = NULL;
+       }
+
+       /* Should free? */
+       if (do_free) {
+               free(current->data);
+       }
+       free(current);
+
+       *list = head;
+       return true;
+}
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonlist.h b/src/shared/buxtonlist.h
new file mode 100644 (file)
index 0000000..6471d70
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#pragma once
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define _cleanup_list_ __attribute__ ((cleanup(buxton_list_free)))
+#define _cleanup_list_all_ __attribute__ ((cleanup(buxton_list_free_all)))
+
+/**
+ * A singly-linked list
+ */
+typedef struct BuxtonList {
+       void *data; /**<Data for this item in the list */
+       struct BuxtonList *next; /**<Next item in the list */
+       struct BuxtonList *tail; /**<Tail of the list */
+       uint64_t size; /**<Size of list */
+} BuxtonList;
+
+#define BUXTON_LIST_FOREACH(list, elem)                                        \
+       for ((elem) = (list); (elem) != NULL; (elem)=(elem)->next)
+
+
+/**
+ * Append data to an existing list, or create a new list if NULL
+ *
+ * @note May return false if memory allocation errors exist
+ * @param list Pointer to BuxtonList pointer
+ * @param data Data pointer to store in the list
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_list_append(BuxtonList **list, void *data);
+
+/*
+ * Prepend data to an existing list, or create a new list if NULL
+ * Much faster than append
+ *
+ * @note May return false if memory allocation errors exist
+ * @param list Pointer to BuxtonList pointer
+ * @param data Data pointer to store in the list
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_list_prepend(BuxtonList **list, void *data);
+
+/**
+ * Remove the given element from the list
+ *
+ * @param list Pointer to a BuxtonList pointer
+ * @param data Remove item with the matching data pointer
+ * @param do_free Whether to free the item
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_list_remove(BuxtonList **list, void *data, bool do_free);
+
+/**
+ * Free all items in the list
+ */
+static inline void buxton_list_free(void *p)
+{
+       BuxtonList *list = *(BuxtonList**)p;
+       if (!list) {
+               return;
+       }
+       BuxtonList *elem, *node = NULL;
+       elem = list;
+       while (elem != NULL) {
+               node = elem->next;
+               free(elem);
+               elem = node;
+       }
+}
+
+/**
+ * Free all items in the list and their items
+ */
+static inline void buxton_list_free_all(void *p)
+{
+       BuxtonList *list = *(BuxtonList**)p;
+       if (!list) {
+               return;
+       }
+       BuxtonList *elem, *node = NULL;
+       elem = list;
+       while (elem != NULL) {
+               free(elem->data);
+               node = elem->next;
+               free(elem);
+               elem = node;
+       }
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonresponse.h b/src/shared/buxtonresponse.h
new file mode 100644 (file)
index 0000000..8d432b8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "buxtonarray.h"
+#include "buxtonkey.h"
+
+/**
+ * Represents daemon's reply to client
+ */
+typedef struct BuxtonResponse {
+       BuxtonArray *data; /**<Array containing BuxtonData elements */
+       BuxtonControlMessage type; /**<Type of message in the response */
+       _BuxtonKey *key; /**<Key used by client to make the request */
+} _BuxtonResponse;
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/buxtonstring.h b/src/shared/buxtonstring.h
new file mode 100644 (file)
index 0000000..b395619
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+/**
+ * Stores a string entity in Buxton
+ */
+typedef struct BuxtonString {
+       char *value; /**<The content of the string */
+       uint32_t length; /**<The length of the string */
+} BuxtonString;
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/configurator.c b/src/shared/configurator.c
new file mode 100644 (file)
index 0000000..9aa0160
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <iniparser.h>
+#include <linux/limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "configurator.h"
+#include "log.h"
+
+/**
+ * Section name in ini file for our configuration
+ */
+#define CONFIG_SECTION "Configuration"
+
+#ifndef HAVE_SECURE_GETENV
+#  ifdef HAVE___SECURE_GETENV
+#    define secure_getenv __secure_getenv
+#  else
+#    error neither secure_getenv nor __secure_getenv is available
+#  endif
+#endif
+
+/**
+ * @internal
+ * internal state of configurator
+ */
+static struct _conf {
+       bool initialized;       /**< track if it's been init'd */
+       char* keys[CONFIG_MAX]; /**< bag of values */
+       dictionary* ini;        /**< ini parser dictionary */
+} conf = {
+       .initialized = false,
+       .keys = {0},
+       .ini = NULL
+};
+
+/**
+ * @internal
+ * config keys as in environment
+ * @fixme name sucks
+ */
+static const char *KS[CONFIG_MAX] = {
+       NULL,
+       "BUXTON_CONF_FILE",
+       "BUXTON_MODULE_DIR",
+       "BUXTON_DB_PATH",
+       "BUXTON_SMACK_LOAD_FILE",
+       "BUXTON_BUXTON_SOCKET"
+};
+
+/**
+ * @internal
+ * keys as they are used in config file
+ */
+static const char *config_keys[CONFIG_MAX] = {
+       NULL,
+       NULL,                   /**< conf file entry in config file is meaningless */
+       "ModuleDirectory",
+       "DatabasePath",
+       "SmackLoadFile",
+       "SocketPath"
+};
+
+static const char *COMPILE_DEFAULT[CONFIG_MAX] = {
+       NULL,
+       _DEFAULT_CONFIGURATION_FILE,
+       _MODULE_DIRECTORY,
+       _DB_PATH,
+       _SMACK_LOAD_FILE,
+       _BUXTON_SOCKET
+};
+
+/**
+ * @internal
+ * wrap strdup and die if malloc fails
+ *
+ * @note This may seem lame and stupid at first glance. The former is
+ * right the latter is not. This function may abort()
+ *
+ * @return pointer to dup'd string
+ */
+__attribute__ ((pure))
+static inline char *_strdup(const char* string)
+{
+       char* s;
+
+       s = strdup(string);
+       if (s == NULL) {
+               abort();
+       }
+       return s;
+}
+
+/**
+ * @internal
+ * grab a string from the ini file
+ *
+ * @param section the section of the ini file
+ * @param name the name of the key
+ * @param required if key required or not
+ * @param def default value for nonrequired setting
+ *
+ * @note This function may abort()
+ *
+ * @return The value as a string. The the string is internal to
+ * libini, and if at this stage of the game it is null, just die.
+ * Something is really wrong and even if we could recover, the system
+ * is not working correctly.
+ */
+static char *get_ini_string(char *section, char *name, bool required,
+       char *def)
+{
+       char buf[PATH_MAX];
+       char *s;
+
+       assert(conf.ini);
+       snprintf(buf, sizeof(buf), "%s:%s", section, name);
+       s = iniparser_getstring(conf.ini, buf, def);
+       if (s == NULL && required) {
+               abort();
+       }
+       return s;
+}
+
+/**
+ * analagous method to get_ini_string()
+ *
+ * @param section the section of the ini file
+ * @param name the name of the key
+ * @param required if key required or not
+ * @param def default value for nonrequired setting
+ *
+ * @note inlined b/c only used once and why not.
+ *
+ * @return the value
+ */
+static inline int get_ini_int(char *section, char *name, bool required,
+       int def)
+{
+       char buf[PATH_MAX];
+       int exists;
+
+       assert(conf.ini);
+       snprintf(buf, sizeof(buf), "%s:%s", section, name);
+       exists = iniparser_find_entry(conf.ini, buf);
+       if (!exists && required) {
+               abort();
+       }
+       return iniparser_getint(conf.ini, buf, def);
+}
+
+/**
+ * @internal
+ * Initialize conf
+ *
+ */
+static void initialize(void)
+{
+       if (conf.initialized) {
+               return;
+       }
+
+       for (int i= CONFIG_MIN+1; i < CONFIG_MAX; i++) {
+               char *envkey;
+
+               /* hasn't been set through command line */
+               if (conf.keys[i] == NULL) {
+                       /* second priority is env */
+                       envkey = secure_getenv(KS[i]);
+                       if (envkey) {
+                               conf.keys[i] = _strdup(envkey);
+                       }
+               }
+
+               /* third priority is conf file */
+               if (conf.keys[i] == NULL && conf.ini) {
+                       /* for once config file is loaded */
+                       char key[PATH_MAX+strlen(CONFIG_SECTION)+1+1]; /* one for null and one for : */
+                       char *value;
+
+                       snprintf(key, sizeof(key), "%s:%s", CONFIG_SECTION, config_keys[i]);
+                       value = iniparser_getstring(conf.ini, key, NULL);
+                       if (value != NULL) {
+                               conf.keys[i] = _strdup(value);
+                       }
+               }
+
+               if (conf.keys[i] == NULL) {
+                       /* last priority is compile time defaults */
+                       conf.keys[i] = _strdup(COMPILE_DEFAULT[i]);
+               }
+
+               /* if config file is not loaded, load */
+               if (i == CONFIG_CONF_FILE) {
+                       char *path = conf.keys[CONFIG_CONF_FILE];
+
+                       conf.ini = iniparser_load(path);
+                       if (conf.ini == NULL) {
+                               buxton_log("Failed to load buxton conf file: %s\n", path);
+                       }
+               }
+       }
+       conf.initialized = true;
+}
+
+/**
+ * @internal
+ * use a destructor to free everything rather than yucky at_exit
+ * nonsense
+ *
+ * @return
+ */
+__attribute__ ((destructor)) static void free_conf(void)
+{
+       if (!conf.initialized) {
+               return;
+       }
+       for (int i= CONFIG_MIN+1; i < CONFIG_MAX; i++) {
+               free(conf.keys[i]);
+       }
+       iniparser_freedict(conf.ini);
+}
+
+void buxton_add_cmd_line(ConfigKey confkey, char* data)
+{
+       if (confkey >= CONFIG_MAX || confkey <= CONFIG_MIN) {
+               buxton_log("invalid confkey");
+               return;
+       }
+       if (!data) {
+               buxton_log("invalid data");
+               return;
+       }
+
+       conf.keys[confkey] = _strdup(data);
+}
+
+const char* buxton_module_dir(void)
+{
+       initialize();
+       return (const char*)conf.keys[CONFIG_MODULE_DIR];
+}
+
+const char* buxton_conf_file(void)
+{
+       initialize();
+       return (const char*)conf.keys[CONFIG_CONF_FILE];
+}
+
+const char* buxton_db_path(void)
+{
+       initialize();
+       return (const char*)conf.keys[CONFIG_DB_PATH];
+}
+
+const char* buxton_smack_load_file(void)
+{
+       initialize();
+       return (const char*)conf.keys[CONFIG_SMACK_LOAD_FILE];
+}
+
+const char* buxton_socket(void)
+{
+       initialize();
+       return (const char*)conf.keys[CONFIG_BUXTON_SOCKET];
+}
+
+int buxton_key_get_layers(ConfigLayer **layers)
+{
+       ConfigLayer *_layers;
+       int n;
+       int j = 0;
+
+       assert(layers);
+       initialize();
+       if (conf.ini == NULL) {
+               buxton_log("config file not loaded when calling buxton_key_get_layers()");
+               abort();
+       }
+       n = iniparser_getnsec(conf.ini);
+       _layers = (ConfigLayer*)calloc((size_t)n, sizeof(ConfigLayer));
+       if (_layers == NULL) {
+               abort();
+       }
+       for (int i= 0; i < n; i++) {
+               char *section_name;
+
+               section_name = iniparser_getsecname(conf.ini, i);
+               if (!section_name) {
+                       abort();
+               }
+               if (!strcasecmp(section_name, CONFIG_SECTION)) {
+                       continue;
+               }
+               _layers[j].name = section_name;
+               _layers[j].description = get_ini_string(section_name,
+                       "Description", true, NULL);
+               _layers[j].backend = get_ini_string(section_name, "Backend",
+                       true, NULL);
+               _layers[j].type = get_ini_string(section_name, "Type", true,
+                       NULL);
+               _layers[j].priority = get_ini_int(section_name, "Priority",
+                       true, 0);
+               _layers[j].access = get_ini_string(section_name, "Access",
+                       false, "read-write");
+               j++;
+       }
+       *layers = _layers;
+       return j;
+}
+
+void include_configurator(void)
+{
+       ;
+}
+
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/configurator.h b/src/shared/configurator.h
new file mode 100644 (file)
index 0000000..be25a1a
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file configurator.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used to handle the configuration
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+typedef enum ConfigKey {
+       CONFIG_MIN = 0,
+       CONFIG_CONF_FILE,
+       CONFIG_MODULE_DIR,
+       CONFIG_DB_PATH,
+       CONFIG_SMACK_LOAD_FILE,
+       CONFIG_BUXTON_SOCKET,
+       CONFIG_MAX
+} ConfigKey;
+
+/**
+ * Slightly duplicative of BuxtonLayer, but defined here instead of
+ * there. This will probably be deprecated for BuxtonLayer once
+ * things are integrated.
+ */
+typedef struct ConfigLayer {
+       char *name;
+       char *type;
+       char *backend;
+       char *description;
+       char *access;
+       int priority;
+} ConfigLayer;
+
+/**
+ * @internal
+ * @brief Add command line data
+ *
+ * @note This API is draft
+ */
+void buxton_add_cmd_line(ConfigKey confkey, char* data);
+
+/**
+ * @internal
+ * @brief Get the directory of plugin modules
+ *
+ * @return the path to the plugin module. Do not free this pointer.
+ * It belongs to configurator.
+ */
+const char *buxton_module_dir(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * @internal
+ * @brief Get the path of the config file
+ *
+ * @return the path of the config file. Do not free this pointer.
+ * It belongs to configurator.
+ */
+const char *buxton_conf_file(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * @internal
+ * @brief Get the path of the buxton database
+ *
+ *
+ * @return the path of the database file. Do not free this pointer.
+ * It belongs to configurator.
+ */
+const char *buxton_db_path(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * @internal
+ * @brief Get the path of the smack load file.
+ *
+ *
+ * @return the path of the smack load file. Do not free this pointer.
+ * It belongs to configurator.
+ */
+const char *buxton_smack_load_file(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * @internal
+ * @brief Get the path of the buxton socket.
+ *
+ *
+ * @return the path of the buxton socket. Do not free this pointer.
+ * It belongs to configurator.
+ */
+const char *buxton_socket(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * @internal
+ * @brief Get an array of ConfigLayers from the conf file
+ *
+ * @param layers pointer to a pointer where the array of ConfigLayers
+ * will be stored. Callers should free this pointer with free when
+ * they are done with it.
+ *
+ * @return an integer that indicates the number of layers.
+ */
+int buxton_key_get_layers(ConfigLayer **layers)
+       __attribute__((warn_unused_result));
+
+void include_configurator(void);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/dictionary.c b/src/shared/dictionary.c
new file mode 100644 (file)
index 0000000..058f4e0
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+   Copyright (c) 2000-2011 by Nicolas Devillard.
+   MIT License
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.c
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ    1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ   128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY    ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                            Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, size_t size)
+{
+    void * newptr ;
+
+    newptr = calloc(2*size, 1);
+    if (newptr==NULL) {
+        return NULL ;
+    }
+    memcpy(newptr, ptr, size);
+    free(ptr);
+    return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Duplicate a string
+  @param    s String to duplicate
+  @return   Pointer to a newly allocated string, to be freed with free()
+
+  This is a replacement for strdup(). This implementation is provided
+  for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+    char * t ;
+    if (!s)
+        return NULL ;
+    t = (char*)malloc(strlen(s)+1) ;
+    if (t) {
+        strcpy(t,s);
+    }
+    return t ;
+}
+
+/*---------------------------------------------------------------------------
+                            Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+    size_t      len ;
+    unsigned    hash ;
+    size_t      i ;
+
+    len = strlen(key);
+    for (hash=0, i=0 ; i<len ; i++) {
+        hash += (unsigned)key[i] ;
+        hash += (hash<<10);
+        hash ^= (hash>>6) ;
+    }
+    hash += (hash <<3);
+    hash ^= (hash >>11);
+    hash += (hash <<15);
+    return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size)
+{
+    dictionary  *   d ;
+
+    /* If no size was specified, allocate space for DICTMINSZ */
+    if (size<DICTMINSZ) size=DICTMINSZ ;
+
+    if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+        return NULL;
+    }
+    d->size = size ;
+    d->val  = (char **)calloc(size, sizeof(char*));
+    d->key  = (char **)calloc(size, sizeof(char*));
+    d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+    return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+    int     i ;
+
+    if (d==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]!=NULL)
+            free(d->key[i]);
+        if (d->val[i]!=NULL)
+            free(d->val[i]);
+    }
+    free(d->val);
+    free(d->key);
+    free(d->hash);
+    free(d);
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+    unsigned    hash ;
+    int         i ;
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                return d->val[i] ;
+            }
+        }
+    }
+    return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+    int         i ;
+    unsigned    hash ;
+
+    if (d==NULL || key==NULL) return -1 ;
+
+    /* Compute hash for this key */
+    hash = dictionary_hash(key) ;
+    /* Find if value is already in dictionary */
+    if (d->n>0) {
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            if (hash==d->hash[i]) { /* Same hash value */
+                if (!strcmp(key, d->key[i])) {   /* Same key */
+                    /* Found a value: modify and return */
+                    if (d->val[i]!=NULL)
+                        free(d->val[i]);
+                    d->val[i] = val ? xstrdup(val) : NULL ;
+                    /* Value has been modified: return */
+                    return 0 ;
+                }
+            }
+        }
+    }
+    /* Add a new value */
+    /* See if dictionary needs to grow */
+    if (d->n==d->size) {
+
+        /* Reached maximum size: reallocate dictionary */
+        d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ;
+        d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ;
+        d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+            /* Cannot grow dictionary */
+            return -1 ;
+        }
+        /* Double size */
+        d->size *= 2 ;
+    }
+
+    /* Insert key in the first empty slot. Start at d->n and wrap at
+       d->size. Because d->n < d->size this will necessarily
+       terminate. */
+    for (i=d->n ; d->key[i] ; ) {
+        if(++i == d->size) i = 0;
+    }
+    /* Copy key */
+    d->key[i]  = xstrdup(key);
+    d->val[i]  = val ? xstrdup(val) : NULL ;
+    d->hash[i] = hash;
+    d->n ++ ;
+    return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+    unsigned    hash ;
+    int         i ;
+
+    if (key == NULL) {
+        return;
+    }
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                /* Found key */
+                break ;
+            }
+        }
+    }
+    if (i>=d->size)
+        /* Key not found */
+        return ;
+
+    free(d->key[i]);
+    d->key[i] = NULL ;
+    if (d->val[i]!=NULL) {
+        free(d->val[i]);
+        d->val[i] = NULL ;
+    }
+    d->hash[i] = 0 ;
+    d->n -- ;
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+    int     i ;
+
+    if (d==NULL || out==NULL) return ;
+    if (d->n<1) {
+        fprintf(out, "empty dictionary\n");
+        return ;
+    }
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]) {
+            fprintf(out, "%20s\t[%s]\n",
+                    d->key[i],
+                    d->val[i] ? d->val[i] : "UNDEF");
+        }
+    }
+    return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+    dictionary  *   d ;
+    char    *   val ;
+    int         i ;
+    char        cval[90] ;
+
+    /* Allocate dictionary */
+    printf("allocating...\n");
+    d = dictionary_new(0);
+
+    /* Set values in dictionary */
+    printf("setting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_set(d, cval, "salut");
+    }
+    printf("getting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        val = dictionary_get(d, cval, DICT_INVALID_KEY);
+        if (val==DICT_INVALID_KEY) {
+            printf("cannot get value for key [%s]\n", cval);
+        }
+    }
+    printf("unsetting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_unset(d, cval);
+    }
+    if (d->n != 0) {
+        printf("error deleting values\n");
+    }
+    printf("deallocating...\n");
+    dictionary_del(d);
+    return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/src/shared/dictionary.h b/src/shared/dictionary.h
new file mode 100644 (file)
index 0000000..1f840cb
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+   Copyright (c) 2000-2011 by Nicolas Devillard.
+   MIT License
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.h
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*---------------------------------------------------------------------------
+                                New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dictionary object
+
+  This object contains a list of string/string associations. Each
+  association is identified by a unique string key. Looking up values
+  in the dictionary is speeded up by the use of a (hopefully collision-free)
+  hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+    int             n ;     /** Number of entries in dictionary */
+    size_t          size ;  /** Storage size */
+    char        **  val ;   /** List of string values */
+    char        **  key ;   /** List of string keys */
+    unsigned     *  hash ;  /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+                            Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
diff --git a/src/shared/direct.c b/src/shared/direct.c
new file mode 100644 (file)
index 0000000..878ad7b
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "direct.h"
+#include "log.h"
+#include "smack.h"
+#include "util.h"
+
+#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK"
+
+bool buxton_direct_open(BuxtonControl *control)
+{
+
+       assert(control);
+
+       memzero(&(control->config), sizeof(BuxtonConfig));
+       buxton_init_layers(&(control->config));
+
+       control->client.direct = true;
+       control->client.pid = getpid();
+
+       return true;
+}
+
+int32_t buxton_direct_get_value(BuxtonControl *control, _BuxtonKey *key,
+                            BuxtonData *data, BuxtonString *data_label,
+                            BuxtonString *client_label)
+{
+       /* Handle direct manipulation */
+       BuxtonLayer *l;
+       BuxtonConfig *config;
+       BuxtonString layer = (BuxtonString){ NULL, 0 };
+       Iterator i;
+       BuxtonData d;
+       int priority = 0;
+       int32_t ret;
+       BuxtonLayerType layer_origin = -1;
+
+       assert(control);
+       assert(key);
+
+       if (key->layer.value) {
+               ret = (int32_t)buxton_direct_get_value_for_layer(control, key, data,
+                                                     data_label,
+                                                     client_label);
+               return ret;
+       }
+
+       config = &control->config;
+
+       HASHMAP_FOREACH(l, config->layers, i) {
+               key->layer.value = l->name.value;
+               key->layer.length = l->name.length;
+               ret = (int32_t)buxton_direct_get_value_for_layer(control,
+                                                     key,
+                                                     &d,
+                                                     data_label,
+                                                     client_label);
+               if (!ret) {
+                       free(data_label->value);
+                       data_label->value = NULL;
+                       data_label->length = 0;
+                       if (d.type == STRING) {
+                               free(d.store.d_string.value);
+                       }
+
+                       if ((l->type == LAYER_SYSTEM && (layer_origin != LAYER_SYSTEM ||
+                                                        priority <= l->priority)) ||
+                           (l->type == LAYER_USER && layer_origin != LAYER_SYSTEM &&
+                            priority <= l->priority)) {
+                               if (l->type == LAYER_SYSTEM) {
+                                       layer_origin = LAYER_SYSTEM;
+                               } else {
+                                       layer_origin = LAYER_USER;
+                               }
+                               priority = l->priority;
+                               layer.value = l->name.value;
+                               layer.length = l->name.length;
+                       }
+               }
+       }
+       if (layer.value) {
+               key->layer.value = layer.value;
+               key->layer.length = layer.length;
+               ret = (int32_t)buxton_direct_get_value_for_layer(control,
+                                                     key,
+                                                     data,
+                                                     data_label,
+                                                     client_label);
+               key->layer.value = NULL;
+               key->layer.length = 0;
+
+               return ret;
+       }
+       return ENOENT;
+}
+
+int buxton_direct_get_value_for_layer(BuxtonControl *control,
+                                      _BuxtonKey *key,
+                                      BuxtonData *data,
+                                      BuxtonString *data_label,
+                                      BuxtonString *client_label)
+{
+       /* Handle direct manipulation */
+       BuxtonBackend *backend = NULL;
+       BuxtonLayer *layer = NULL;
+       BuxtonConfig *config;
+       BuxtonData g;
+       _BuxtonKey group;
+       BuxtonString group_label;
+       int ret;
+
+       assert(control);
+       assert(key);
+       assert(data_label);
+
+       buxton_debug("get_value '%s:%s' for layer '%s' start\n",
+                    key->group.value, key->name.value, key->layer.value);
+
+       memzero(&g, sizeof(BuxtonData));
+       memzero(&group, sizeof(_BuxtonKey));
+       memzero(&group_label, sizeof(BuxtonString));
+
+       if (!key->layer.value) {
+               ret = EINVAL;
+               goto fail;
+       }
+
+       config = &control->config;
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               ret = EINVAL;
+               goto fail;
+       }
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+
+       /* Groups must be created first, so bail if this key's group doesn't exist */
+       if (key->name.value) {
+               if (!buxton_copy_key_group(key, &group)) {
+                       abort();
+               }
+               ret = buxton_direct_get_value_for_layer(control, &group, &g, &group_label, NULL);
+               if (ret) {
+                       buxton_debug("Group %s for name %s missing for get value\n", key->group.value, key->name.value);
+                       goto fail;
+               }
+       }
+
+       /* The group checks are only needed for key lookups, or we recurse endlessly */
+       if (key->name.value && client_label) {
+               if (!buxton_check_smack_access(client_label, &group_label, ACCESS_READ)) {
+                       ret = EPERM;
+                       goto fail;
+               }
+       }
+
+       ret = backend->get_value(layer, key, data, data_label);
+       if (!ret) {
+               /* Access checks are not needed for direct clients, where client_label is NULL */
+               if (data_label->value && client_label && client_label->value &&
+                   !buxton_check_smack_access(client_label, data_label, ACCESS_READ)) {
+                       /* Client lacks permission to read the value */
+                       free(data_label->value);
+                       ret = EPERM;
+                       goto fail;
+               }
+       }
+
+fail:
+       free(g.store.d_string.value);
+       free(group.group.value);
+       free(group.name.value);
+       free(group.layer.value);
+       free(group_label.value);
+       buxton_debug("get_value '%s:%s' for layer '%s' end\n",
+                    key->group.value, key->name.value, key->layer.value);
+       return ret;
+}
+
+bool buxton_direct_set_value(BuxtonControl *control,
+                            _BuxtonKey *key,
+                            BuxtonData *data,
+                            BuxtonString *label)
+{
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+       BuxtonString default_label = buxton_string_pack("_");
+       BuxtonString *l;
+       _cleanup_buxton_data_ BuxtonData *d = NULL;
+       _cleanup_buxton_data_ BuxtonData *g = NULL;
+       _cleanup_buxton_key_ _BuxtonKey *group = NULL;
+       _cleanup_buxton_string_ BuxtonString *data_label = NULL;
+       _cleanup_buxton_string_ BuxtonString *group_label = NULL;
+       bool r = false;
+       int ret;
+
+       assert(control);
+       assert(key);
+       assert(data);
+
+       buxton_debug("set_value start\n");
+
+       group = malloc0(sizeof(_BuxtonKey));
+       if (!group) {
+               abort();
+       }
+       g = malloc0(sizeof(BuxtonData));
+       if (!g) {
+               abort();
+       }
+       group_label = malloc0(sizeof(BuxtonString));
+       if (!group_label) {
+               abort();
+       }
+
+       d = malloc0(sizeof(BuxtonData));
+       if (!d) {
+               abort();
+       }
+       data_label = malloc0(sizeof(BuxtonString));
+       if (!data_label) {
+               abort();
+       }
+
+       /* Groups must be created first, so bail if this key's group doesn't exist */
+       if (!buxton_copy_key_group(key, group)) {
+               abort();
+       }
+
+       ret = buxton_direct_get_value_for_layer(control, group, g, group_label, NULL);
+       if (ret) {
+               buxton_debug("Error(%d): %s\n", ret, strerror(ret));
+               buxton_debug("Group %s for name %s missing for set value\n", key->group.value, key->name.value);
+               goto fail;
+       }
+
+       /* Access checks are not needed for direct clients, where label is NULL */
+       if (label) {
+               if (!buxton_check_smack_access(label, group_label, ACCESS_WRITE)) {
+                       goto fail;
+               }
+
+               ret = buxton_direct_get_value_for_layer(control, key, d, data_label, NULL);
+               if (ret == -ENOENT || ret == EINVAL) {
+                       goto fail;
+               }
+               if (!ret) {
+                       if (!buxton_check_smack_access(label, data_label, ACCESS_WRITE)) {
+                               goto fail;
+                       }
+                       l = data_label;
+               } else {
+                       l = label;
+               }
+       } else {
+               ret = buxton_direct_get_value_for_layer(control, key, d, data_label, NULL);
+               if (ret == -ENOENT || ret == EINVAL) {
+                       goto fail;
+               } else if (!ret) {
+                       l = data_label;
+               } else {
+                       l = &default_label;
+               }
+       }
+
+       config = &control->config;
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               goto fail;
+       }
+
+       if (layer->readonly) {
+               buxton_debug("Read-only layer!\n");
+               goto fail;
+       }
+
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+       ret = backend->set_value(layer, key, data, l);
+       if (ret) {
+               buxton_debug("set value failed: %s\n", strerror(ret));
+       } else {
+               r = true;
+       }
+
+fail:
+       buxton_debug("set_value end\n");
+       return r;
+}
+
+bool buxton_direct_set_label(BuxtonControl *control,
+                            _BuxtonKey *key,
+                            BuxtonString *label)
+{
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+       bool r = false;
+       int ret;
+
+       assert(control);
+       assert(key);
+       assert(label);
+
+       config = &control->config;
+
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               goto fail;
+       }
+
+       if (layer->readonly) {
+               buxton_debug("Read-only layer!\n");
+               goto fail;
+       }
+
+       if (layer->type == LAYER_SYSTEM) {
+               char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+               bool skip_check = (root_check && streq(root_check, "0"));
+
+               /* FIXME: should check client's capability set instead of UID */
+               if (control->client.uid != 0 && !skip_check) {
+                       buxton_debug("Not permitted to create group '%s'\n", key->group.value);
+                       goto fail;
+               }
+       } else {
+               buxton_debug("Cannot set labels in a user layer\n");
+               goto fail;
+       }
+
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+       ret = backend->set_value(layer, key, NULL, label);
+       if (ret) {
+               buxton_debug("set label failed: %s\n", strerror(ret));
+       } else {
+               r = true;
+       }
+
+fail:
+       return r;
+}
+
+bool buxton_direct_create_group(BuxtonControl *control,
+                               _BuxtonKey *key,
+                               BuxtonString *label)
+{
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+       BuxtonString s, l;
+       _cleanup_buxton_data_ BuxtonData *data = NULL;
+       _cleanup_buxton_data_ BuxtonData *group = NULL;
+       _cleanup_buxton_string_ BuxtonString *dlabel = NULL;
+       _cleanup_buxton_string_ BuxtonString *glabel = NULL;
+       bool r = false;
+       int ret;
+
+       assert(control);
+       assert(key);
+
+       data = malloc0(sizeof(BuxtonData));
+       if (!data) {
+               abort();
+       }
+       group = malloc0(sizeof(BuxtonData));
+       if (!group) {
+               abort();
+       }
+       dlabel = malloc0(sizeof(BuxtonString));
+       if (!dlabel) {
+               abort();
+       }
+       glabel = malloc0(sizeof(BuxtonString));
+       if (!glabel) {
+               abort();
+       }
+
+       config = &control->config;
+
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               goto fail;
+       }
+
+       if (layer->readonly) {
+               buxton_debug("Read-only layer!\n");
+               goto fail;
+       }
+
+       if (layer->type == LAYER_SYSTEM) {
+               char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+               bool skip_check = (root_check && streq(root_check, "0"));
+
+               /* FIXME: should check client's capability set instead of UID */
+               if (control->client.uid != 0 && !skip_check) {
+                       buxton_debug("Not permitted to create group '%s'\n", key->group.value);
+                       goto fail;
+               }
+       }
+
+       if (buxton_direct_get_value_for_layer(control, key, group, glabel, NULL) != ENOENT) {
+               buxton_debug("Group '%s' already exists\n", key->group.value);
+               goto fail;
+       }
+
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       /* Since groups don't have a value, we create a dummy value */
+       data->type = STRING;
+       s = buxton_string_pack("BUXTON_GROUP_VALUE");
+       if (!buxton_string_copy(&s, &data->store.d_string)) {
+               abort();
+       }
+
+       if (label) {
+               if (!buxton_string_copy(label, dlabel)) {
+                       abort();
+               }
+       } else {
+               /* _ (floor) is our current default label */
+               l = buxton_string_pack("_");
+               if (!buxton_string_copy(&l, dlabel)) {
+                       abort();
+               }
+       }
+
+       layer->uid = control->client.uid;
+       ret = backend->set_value(layer, key, data, dlabel);
+       if (ret) {
+               buxton_debug("create group failed: %s\n", strerror(ret));
+       } else {
+               r = true;
+       }
+
+fail:
+       return r;
+}
+
+bool buxton_direct_remove_group(BuxtonControl *control,
+                               _BuxtonKey *key,
+                               BuxtonString *client_label)
+{
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+       _cleanup_buxton_data_ BuxtonData *group = NULL;
+       _cleanup_buxton_string_ BuxtonString *glabel = NULL;
+       bool r = false;
+       int ret;
+
+       assert(control);
+       assert(key);
+
+       group = malloc0(sizeof(BuxtonData));
+       if (!group) {
+               abort();
+       }
+       glabel = malloc0(sizeof(BuxtonString));
+       if (!glabel) {
+               abort();
+       }
+
+       config = &control->config;
+
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               goto fail;
+       }
+
+       if (layer->readonly) {
+               buxton_debug("Read-ony layer!\n");
+               goto fail;
+       }
+
+       if (layer->type == LAYER_SYSTEM) {
+               char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+               bool skip_check = (root_check && streq(root_check, "0"));
+
+               /* FIXME: should check client's capability set instead of UID */
+               if (control->client.uid != 0 && !skip_check) {
+                       buxton_debug("Not permitted to remove group '%s'\n", key->group.value);
+                       goto fail;
+               }
+       }
+
+       if (buxton_direct_get_value_for_layer(control, key, group, glabel, NULL)) {
+               buxton_debug("Group '%s' doesn't exist\n", key->group.value);
+               goto fail;
+       }
+
+       if (layer->type == LAYER_USER) {
+               if (client_label && !buxton_check_smack_access(client_label, glabel, ACCESS_WRITE)) {
+                       goto fail;
+               }
+       }
+
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+
+       ret = backend->unset_value(layer, key, NULL, NULL);
+       if (ret) {
+               buxton_debug("remove group failed: %s\n", strerror(ret));
+       } else {
+               r = true;
+       }
+
+fail:
+       return r;
+}
+
+bool buxton_direct_list_keys(BuxtonControl *control,
+                            BuxtonString *layer_name,
+                            BuxtonArray **list)
+{
+       assert(control);
+       assert(layer_name);
+
+       /* Handle direct manipulation */
+       BuxtonBackend *backend = NULL;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+
+       config = &control->config;
+       if ((layer = hashmap_get(config->layers, layer_name->value)) == NULL) {
+               return false;
+       }
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+       return backend->list_keys(layer, list);
+}
+
+bool buxton_direct_unset_value(BuxtonControl *control,
+                              _BuxtonKey *key,
+                              BuxtonString *label)
+{
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonConfig *config;
+       _cleanup_buxton_string_ BuxtonString *data_label = NULL;
+       _cleanup_buxton_string_ BuxtonString *group_label = NULL;
+       _cleanup_buxton_data_ BuxtonData *d = NULL;
+       _cleanup_buxton_data_ BuxtonData *g = NULL;
+       _cleanup_buxton_key_ _BuxtonKey *group = NULL;
+       int ret;
+       bool r = false;
+
+       assert(control);
+       assert(key);
+
+       group = malloc0(sizeof(_BuxtonKey));
+       if (!group) {
+               abort();
+       }
+       g = malloc0(sizeof(BuxtonData));
+       if (!g) {
+               abort();
+       }
+       group_label = malloc0(sizeof(BuxtonString));
+       if (!group_label) {
+               abort();
+       }
+
+       d = malloc0(sizeof(BuxtonData));
+       if (!d) {
+               abort();
+       }
+       data_label = malloc0(sizeof(BuxtonString));
+       if (!data_label) {
+               abort();
+       }
+
+       if (!buxton_copy_key_group(key, group)) {
+               abort();
+       }
+
+       if (buxton_direct_get_value_for_layer(control, group, g, group_label, NULL)) {
+               buxton_debug("Group %s for name %s missing for unset value\n", key->group.value, key->name.value);
+               goto fail;
+       }
+
+       /* Access checks are not needed for direct clients, where label is NULL */
+       if (label) {
+               if (!buxton_check_smack_access(label, group_label, ACCESS_WRITE)) {
+                       goto fail;
+               }
+               if (!buxton_direct_get_value_for_layer(control, key, d, data_label, NULL)) {
+                       if (!buxton_check_smack_access(label, data_label, ACCESS_WRITE)) {
+                               goto fail;
+                       }
+               } else {
+                       buxton_debug("Key %s not found, so unset fails\n", key->name.value);
+                       goto fail;
+               }
+       }
+
+       config = &control->config;
+       if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) {
+               return false;
+       }
+
+       if (layer->readonly) {
+               buxton_debug("Read-only layer!\n");
+               return false;
+       }
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       layer->uid = control->client.uid;
+       ret = backend->unset_value(layer, key, NULL, NULL);
+       if (ret) {
+               buxton_debug("Unset value failed: %s\n", strerror(ret));
+       } else {
+               r = true;
+       }
+
+fail:
+       return r;
+}
+
+bool buxton_direct_init_db(BuxtonControl *control, BuxtonString *layer_name)
+{
+       BuxtonBackend *backend;
+       BuxtonConfig *config;
+       BuxtonLayer *layer;
+       bool ret = false;
+       void *db;
+
+       assert(control);
+       assert(layer_name);
+
+       config = &control->config;
+       layer = hashmap_get(config->layers, layer_name->value);
+       if (!layer) {
+               goto end;
+       }
+
+       if (layer->type == LAYER_USER) {
+               ret = true;
+               goto end;
+       }
+
+       backend = backend_for_layer(config, layer);
+       assert(backend);
+
+       db = backend->create_db(layer);
+       if (db) {
+               ret = true;
+       }
+
+end:
+       return ret;
+}
+
+void buxton_direct_close(BuxtonControl *control)
+{
+       Iterator iterator;
+       BuxtonBackend *backend;
+       BuxtonLayer *layer;
+       BuxtonString *key;
+
+       control->client.direct = false;
+
+       HASHMAP_FOREACH(backend, control->config.backends, iterator) {
+               destroy_backend(backend);
+       }
+       hashmap_free(control->config.backends);
+       hashmap_free(control->config.databases);
+
+       HASHMAP_FOREACH_KEY(layer, key, control->config.layers, iterator) {
+               hashmap_remove(control->config.layers, key);
+               free(layer->name.value);
+               free(layer->description);
+               free(layer);
+       }
+       hashmap_free(control->config.layers);
+
+       control->client.direct = false;
+       control->config.backends = NULL;
+       control->config.databases = NULL;
+       control->config.layers = NULL;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/direct.h b/src/shared/direct.h
new file mode 100644 (file)
index 0000000..00b5ee7
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file direct.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used by the daemon and buxtonctl for talking to the backend
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <backend.h>
+#include "buxton.h"
+#include "hashmap.h"
+
+/**
+ * Open a direct connection to Buxton
+ *
+ * @param control Valid BuxtonControl instance
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_direct_open(BuxtonControl *control)
+       __attribute__((warn_unused_result));
+
+/**
+ * Create a DB for a given layer in Buxton
+ *
+ * @param control Valid BuxtonControl instance
+ * @param layer_name BuxtonString of the layer name to create
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_direct_init_db(BuxtonControl *control, BuxtonString *layer_name)
+       __attribute__((warn_unused_result));
+
+/**
+ * Close direct Buxton management connection
+ * @param control Valid BuxtonControl instance
+ */
+void buxton_direct_close(BuxtonControl *control);
+
+/**
+ * Set a value within Buxton
+ * @param control An initialized control structure
+ * @param key The key struct
+ * @param label A BuxtonString containing the label to set
+ * @return A boolean value, indicating success of the operation
+ */
+bool buxton_direct_set_label(BuxtonControl *control,
+                            _BuxtonKey *key,
+                            BuxtonString *label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Create a group within Buxton
+ * @param control An initialized control structure
+ * @param key The key struct with group and layer members initialized
+ * @param label The Smack label of the client
+ * @return A boolean value, indicating success of the operation
+ */
+bool buxton_direct_create_group(BuxtonControl *control,
+                               _BuxtonKey *key,
+                               BuxtonString *label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Remove a group within Buxton
+ * @param control An initialized control structure
+ * @param key The key struct with group and layer members initialized
+ * @param client_label The Smack label of the client
+ * @return A boolean value, indicating success of the operation
+ */
+bool buxton_direct_remove_group(BuxtonControl *control,
+                               _BuxtonKey *key,
+                               BuxtonString *client_label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Set a value within Buxton
+ * @param control An initialized control structure
+ * @param key The key struct
+ * @param data A struct containing the data to set
+ * @param label The Smack label for the client
+ * @return A boolean value, indicating success of the operation
+ */
+bool buxton_direct_set_value(BuxtonControl *control,
+                            _BuxtonKey *key,
+                            BuxtonData *data,
+                            BuxtonString *label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Retrieve a value from Buxton
+ * @param control An initialized control structure
+ * @param key The key to retrieve
+ * @param data An empty BuxtonData, where data is stored
+ * @param data_label The Smack label of the data
+ * @param client_label The Smack label of the client
+ * @return A int32_t value, indicating success of the operation
+ */
+int32_t buxton_direct_get_value(BuxtonControl *control,
+                            _BuxtonKey *key,
+                            BuxtonData *data,
+                            BuxtonString *data_label,
+                            BuxtonString *client_label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Retrieve a value from Buxton by layer
+ * @param control An initialized control structure
+ * @param key The key to retrieve
+ * @param data An empty BuxtonData, where data is stored
+ * @param data_label The Smack label of the data
+ * @param client_label The Smack label of the client
+ * @return An int value, indicating success of the operation
+ */
+int buxton_direct_get_value_for_layer(BuxtonControl *control,
+                                      _BuxtonKey *key,
+                                      BuxtonData *data,
+                                      BuxtonString *data_label,
+                                      BuxtonString *client_label)
+       __attribute__((warn_unused_result));
+
+/**
+ * Retrieve a list of keys from Buxton
+ * @param control An initialized control structure
+ * @param layer_name The layer to pquery
+ * @param data An empty BuxtonArray, where results are stored
+ * @return A boolean value, indicating success of the operation
+ */
+bool buxton_direct_list_keys(BuxtonControl *control,
+                            BuxtonString *layer,
+                            BuxtonArray **list)
+       __attribute__((warn_unused_result));
+
+/**
+ * Unset a value by key in the given BuxtonLayer
+ * @param control An initialized control structure
+ * @param key The key to remove
+ * @param label The Smack label of the client
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_direct_unset_value(BuxtonControl *control,
+                              _BuxtonKey *key,
+                              BuxtonString *label)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
new file mode 100644 (file)
index 0000000..5a77440
--- /dev/null
@@ -0,0 +1,928 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "macro.h"
+
+#define INITIAL_N_BUCKETS 31
+
+struct hashmap_entry {
+        const void *key;
+        void *value;
+        struct hashmap_entry *bucket_next, *bucket_previous;
+        struct hashmap_entry *iterate_next, *iterate_previous;
+};
+
+struct Hashmap {
+        hash_func_t hash_func;
+        compare_func_t compare_func;
+
+        struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+
+        struct hashmap_entry ** buckets;
+        unsigned n_buckets, n_entries;
+
+        bool from_pool;
+};
+
+struct pool {
+        struct pool *next;
+        unsigned n_tiles;
+        unsigned n_used;
+};
+
+static struct pool *first_hashmap_pool = NULL;
+static void *first_hashmap_tile = NULL;
+
+static struct pool *first_entry_pool = NULL;
+static void *first_entry_tile = NULL;
+
+static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
+        unsigned i;
+
+        /* When a tile is released we add it to the list and simply
+         * place the next pointer at its offset 0. */
+
+        assert(tile_size >= sizeof(void*));
+
+        if (*first_tile) {
+                void *r;
+
+                r = *first_tile;
+                *first_tile = * (void**) (*first_tile);
+                return r;
+        }
+
+        if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
+                unsigned n;
+                size_t size;
+                struct pool *p;
+
+                n = *first_pool ? (*first_pool)->n_tiles : 0;
+                n = MAX(512U, n * 2);
+                size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
+                n = (unsigned)((size - ALIGN(sizeof(struct pool))) / tile_size);
+
+                p = malloc0(size);
+                if (!p)
+                        return NULL;
+
+                p->next = *first_pool;
+                p->n_tiles = n;
+                p->n_used = 0;
+
+                *first_pool = p;
+        }
+
+        i = (*first_pool)->n_used++;
+
+        return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
+}
+
+static void deallocate_tile(void **first_tile, void *p) {
+        * (void**) p = *first_tile;
+        *first_tile = p;
+}
+
+#ifdef VALGRIND
+
+static void drop_pool(struct pool *p) {
+        while (p) {
+                struct pool *n;
+                n = p->next;
+                free(p);
+                p = n;
+        }
+}
+
+__attribute__((destructor)) static void cleanup_pool(void) {
+        /* Be nice to valgrind */
+
+        drop_pool(first_hashmap_pool);
+        drop_pool(first_entry_pool);
+}
+
+#endif
+
+unsigned string_hash_func(const void *p) {
+        unsigned hash = 5381;
+        const signed char *c;
+
+        /* DJB's hash function */
+
+        for (c = p; *c; c++)
+                hash = (hash << 5) + hash + (unsigned) *c;
+
+        return hash;
+}
+
+int string_compare_func(const void *a, const void *b) {
+        return strcmp(a, b);
+}
+
+unsigned trivial_hash_func(const void *p) {
+        return PTR_TO_UINT(p);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+unsigned uint64_hash_func(const void *p) {
+        uint64_t u;
+
+        assert(sizeof(uint64_t) == 2*sizeof(unsigned));
+
+        u = *(const uint64_t*) p;
+
+        return (unsigned) ((u >> 32) ^ u);
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+        uint64_t a, b;
+
+        a = *(const uint64_t*) _a;
+        b = *(const uint64_t*) _b;
+
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
+        Hashmap *h;
+        size_t size;
+
+        size = ALIGN(sizeof(Hashmap)) + INITIAL_N_BUCKETS * sizeof(struct hashmap_entry*);
+
+        h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
+        if (!h)
+                return NULL;
+
+        h->hash_func = hash_func ? hash_func : trivial_hash_func;
+        h->compare_func = compare_func ? compare_func : trivial_compare_func;
+
+        h->n_buckets = INITIAL_N_BUCKETS;
+        h->n_entries = 0;
+        h->iterate_list_head = h->iterate_list_tail = NULL;
+
+       h->buckets = (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)));
+        h->from_pool = true;
+
+        return h;
+}
+
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
+        Hashmap *q;
+
+        assert(h);
+
+        if (*h)
+                return 0;
+
+        q = hashmap_new(hash_func, compare_func);
+        if (!q)
+                return -ENOMEM;
+        *h = q;
+        return 0;
+}
+
+static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+        assert(h);
+        assert(e);
+
+        /* Insert into hash table */
+        e->bucket_next = h->buckets[hash];
+        e->bucket_previous = NULL;
+        if (h->buckets[hash])
+                h->buckets[hash]->bucket_previous = e;
+        h->buckets[hash] = e;
+
+        /* Insert into iteration list */
+        e->iterate_previous = h->iterate_list_tail;
+        e->iterate_next = NULL;
+        if (h->iterate_list_tail) {
+                assert(h->iterate_list_head);
+                h->iterate_list_tail->iterate_next = e;
+        } else {
+                assert(!h->iterate_list_head);
+                h->iterate_list_head = e;
+        }
+        h->iterate_list_tail = e;
+
+        h->n_entries++;
+        assert(h->n_entries >= 1);
+}
+
+static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+        assert(h);
+        assert(e);
+
+        /* Remove from iteration list */
+        if (e->iterate_next)
+                e->iterate_next->iterate_previous = e->iterate_previous;
+        else
+                h->iterate_list_tail = e->iterate_previous;
+
+        if (e->iterate_previous)
+                e->iterate_previous->iterate_next = e->iterate_next;
+        else
+                h->iterate_list_head = e->iterate_next;
+
+        /* Remove from hash table bucket list */
+        if (e->bucket_next)
+                e->bucket_next->bucket_previous = e->bucket_previous;
+
+        if (e->bucket_previous)
+                e->bucket_previous->bucket_next = e->bucket_next;
+        else
+                h->buckets[hash] = e->bucket_next;
+
+        assert(h->n_entries >= 1);
+        h->n_entries--;
+}
+
+static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
+        unsigned hash;
+
+        assert(h);
+        assert(e);
+
+        hash = h->hash_func(e->key) % h->n_buckets;
+
+        unlink_entry(h, e, hash);
+
+        if (h->from_pool)
+                deallocate_tile(&first_entry_tile, e);
+        else
+                free(e);
+}
+
+void hashmap_free(Hashmap*h) {
+
+        /* Free the hashmap, but nothing in it */
+
+        if (!h)
+                return;
+
+        hashmap_clear(h);
+
+        if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
+                free(h->buckets);
+
+        if (h->from_pool)
+                deallocate_tile(&first_hashmap_tile, h);
+        else
+                free(h);
+}
+
+void hashmap_free_free(Hashmap *h) {
+
+        /* Free the hashmap and all data objects in it, but not the
+         * keys */
+
+        if (!h)
+                return;
+
+        hashmap_clear_free(h);
+        hashmap_free(h);
+}
+
+void hashmap_free_free_free(Hashmap *h) {
+
+        /* Free the hashmap and all data and key objects in it */
+
+        if (!h)
+                return;
+
+        hashmap_clear_free_free(h);
+        hashmap_free(h);
+}
+
+void hashmap_clear(Hashmap *h) {
+        if (!h)
+                return;
+
+        while (h->iterate_list_head)
+                remove_entry(h, h->iterate_list_head);
+}
+
+void hashmap_clear_free(Hashmap *h) {
+        void *p;
+
+        if (!h)
+                return;
+
+        while ((p = hashmap_steal_first(h)))
+                free(p);
+}
+
+void hashmap_clear_free_free(Hashmap *h) {
+        if (!h)
+                return;
+
+        while (h->iterate_list_head) {
+                void *a, *b;
+
+                a = h->iterate_list_head->value;
+                b = (void*) h->iterate_list_head->key;
+                remove_entry(h, h->iterate_list_head);
+                free(a);
+                free(b);
+        }
+}
+
+
+static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
+        struct hashmap_entry *e;
+        assert(h);
+        assert(hash < h->n_buckets);
+
+        for (e = h->buckets[hash]; e; e = e->bucket_next)
+                if (h->compare_func(e->key, key) == 0)
+                        return e;
+
+        return NULL;
+}
+
+static bool resize_buckets(Hashmap *h) {
+        unsigned m;
+        struct hashmap_entry **n, *i;
+
+        assert(h);
+
+        if (_likely_(h->n_entries*4 < h->n_buckets*3))
+                return false;
+
+        /* Increase by four */
+        m = (h->n_entries+1)*4-1;
+
+        /* If we hit OOM we simply risk packed hashmaps... */
+        n = new0(struct hashmap_entry*, m);
+        if (!n)
+                return false;
+
+        for (i = h->iterate_list_head; i; i = i->iterate_next) {
+                unsigned hash, x;
+
+                hash = h->hash_func(i->key);
+
+                /* First, drop from old bucket table */
+                if (i->bucket_next)
+                        i->bucket_next->bucket_previous = i->bucket_previous;
+
+                if (i->bucket_previous)
+                        i->bucket_previous->bucket_next = i->bucket_next;
+                else
+                        h->buckets[hash % h->n_buckets] = i->bucket_next;
+
+                /* Then, add to new backet table */
+                x = hash % m;
+
+                i->bucket_next = n[x];
+                i->bucket_previous = NULL;
+                if (n[x])
+                        n[x]->bucket_previous = i;
+                n[x] = i;
+        }
+
+        if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
+                free(h->buckets);
+
+        h->buckets = n;
+        h->n_buckets = m;
+
+        return true;
+}
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+        struct hashmap_entry *e;
+        unsigned hash;
+
+        assert(h);
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (e) {
+                if (e->value == value)
+                        return 0;
+                return -EEXIST;
+        }
+
+        if (resize_buckets(h))
+                hash = h->hash_func(key) % h->n_buckets;
+
+        if (h->from_pool)
+                e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
+        else
+                e = new(struct hashmap_entry, 1);
+
+        if (!e)
+                return -ENOMEM;
+
+        e->key = key;
+        e->value = value;
+
+        link_entry(h, e, hash);
+
+        return 1;
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+        struct hashmap_entry *e;
+        unsigned hash;
+
+        assert(h);
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (e) {
+                e->key = key;
+                e->value = value;
+                return 0;
+        }
+
+        return hashmap_put(h, key, value);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value) {
+        struct hashmap_entry *e;
+        unsigned hash;
+
+        assert(h);
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return -ENOENT;
+
+        e->value = value;
+        return 0;
+}
+
+void* hashmap_get(Hashmap *h, const void *key) {
+        unsigned hash;
+        struct hashmap_entry *e;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return NULL;
+
+        return e->value;
+}
+
+void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
+        unsigned hash;
+        struct hashmap_entry *e;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return NULL;
+
+        if (key2)
+                *key2 = (void*) e->key;
+
+        return e->value;
+}
+
+bool hashmap_contains(Hashmap *h, const void *key) {
+        unsigned hash;
+
+        if (!h)
+                return false;
+
+        hash = h->hash_func(key) % h->n_buckets;
+
+        if (!hash_scan(h, hash, key))
+                return false;
+
+        return true;
+}
+
+void* hashmap_remove(Hashmap *h, const void *key) {
+        struct hashmap_entry *e;
+        unsigned hash;
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+
+        if (!(e = hash_scan(h, hash, key)))
+                return NULL;
+
+        data = e->value;
+        remove_entry(h, e);
+
+        return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+        struct hashmap_entry *e;
+        unsigned old_hash, new_hash;
+
+        if (!h)
+                return -ENOENT;
+
+        old_hash = h->hash_func(old_key) % h->n_buckets;
+        if (!(e = hash_scan(h, old_hash, old_key)))
+                return -ENOENT;
+
+        new_hash = h->hash_func(new_key) % h->n_buckets;
+        if (hash_scan(h, new_hash, new_key))
+                return -EEXIST;
+
+        unlink_entry(h, e, old_hash);
+
+        e->key = new_key;
+        e->value = value;
+
+        link_entry(h, e, new_hash);
+
+        return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+        struct hashmap_entry *e, *k;
+        unsigned old_hash, new_hash;
+
+        if (!h)
+                return -ENOENT;
+
+        old_hash = h->hash_func(old_key) % h->n_buckets;
+        if (!(e = hash_scan(h, old_hash, old_key)))
+                return -ENOENT;
+
+        new_hash = h->hash_func(new_key) % h->n_buckets;
+        if ((k = hash_scan(h, new_hash, new_key)))
+                if (e != k)
+                        remove_entry(h, k);
+
+        unlink_entry(h, e, old_hash);
+
+        e->key = new_key;
+        e->value = value;
+
+        link_entry(h, e, new_hash);
+
+        return 0;
+}
+
+void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+        struct hashmap_entry *e;
+        unsigned hash;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return NULL;
+
+        if (e->value != value)
+                return NULL;
+
+        remove_entry(h, e);
+
+        return value;
+}
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
+        struct hashmap_entry *e;
+
+        assert(i);
+
+        if (!h)
+                goto at_end;
+
+        if (*i == ITERATOR_LAST)
+                goto at_end;
+
+        if (*i == ITERATOR_FIRST && !h->iterate_list_head)
+                goto at_end;
+
+        e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
+
+        if (e->iterate_next)
+                *i = (Iterator) e->iterate_next;
+        else
+                *i = ITERATOR_LAST;
+
+        if (key)
+                *key = e->key;
+
+        return e->value;
+
+at_end:
+        *i = ITERATOR_LAST;
+
+        if (key)
+                *key = NULL;
+
+        return NULL;
+}
+
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
+        struct hashmap_entry *e;
+
+        assert(i);
+
+        if (!h)
+                goto at_beginning;
+
+        if (*i == ITERATOR_FIRST)
+                goto at_beginning;
+
+        if (*i == ITERATOR_LAST && !h->iterate_list_tail)
+                goto at_beginning;
+
+        e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
+
+        if (e->iterate_previous)
+                *i = (Iterator) e->iterate_previous;
+        else
+                *i = ITERATOR_FIRST;
+
+        if (key)
+                *key = e->key;
+
+        return e->value;
+
+at_beginning:
+        *i = ITERATOR_FIRST;
+
+        if (key)
+                *key = NULL;
+
+        return NULL;
+}
+
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
+        unsigned hash;
+        struct hashmap_entry *e;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return NULL;
+
+        *i = (Iterator) e;
+
+        return e->value;
+}
+
+void* hashmap_first(Hashmap *h) {
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        return h->iterate_list_head->value;
+}
+
+void* hashmap_first_key(Hashmap *h) {
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        return (void*) h->iterate_list_head->key;
+}
+
+void* hashmap_last(Hashmap *h) {
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_tail)
+                return NULL;
+
+        return h->iterate_list_tail->value;
+}
+
+void* hashmap_steal_first(Hashmap *h) {
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        data = h->iterate_list_head->value;
+        remove_entry(h, h->iterate_list_head);
+
+        return data;
+}
+
+void* hashmap_steal_first_key(Hashmap *h) {
+        void *key;
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        key = (void*) h->iterate_list_head->key;
+        remove_entry(h, h->iterate_list_head);
+
+        return key;
+}
+
+unsigned hashmap_size(Hashmap *h) {
+
+        if (!h)
+                return 0;
+
+        return h->n_entries;
+}
+
+unsigned hashmap_buckets(Hashmap *h) {
+
+        if (!h)
+                return 0;
+
+        return h->n_buckets;
+}
+
+bool hashmap_isempty(Hashmap *h) {
+
+        if (!h)
+                return true;
+
+        return h->n_entries == 0;
+}
+
+int hashmap_merge(Hashmap *h, Hashmap *other) {
+        struct hashmap_entry *e;
+
+        assert(h);
+
+        if (!other)
+                return 0;
+
+        for (e = other->iterate_list_head; e; e = e->iterate_next) {
+                int r;
+
+                if ((r = hashmap_put(h, e->key, e->value)) < 0)
+                        if (r != -EEXIST)
+                                return r;
+        }
+
+        return 0;
+}
+
+void hashmap_move(Hashmap *h, Hashmap *other) {
+        struct hashmap_entry *e, *n;
+
+        assert(h);
+
+        /* The same as hashmap_merge(), but every new item from other
+         * is moved to h. This function is guaranteed to succeed. */
+
+        if (!other)
+                return;
+
+        for (e = other->iterate_list_head; e; e = n) {
+                unsigned h_hash, other_hash;
+
+                n = e->iterate_next;
+
+                h_hash = h->hash_func(e->key) % h->n_buckets;
+
+                if (hash_scan(h, h_hash, e->key))
+                        continue;
+
+                other_hash = other->hash_func(e->key) % other->n_buckets;
+
+                unlink_entry(other, e, other_hash);
+                link_entry(h, e, h_hash);
+        }
+}
+
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+        unsigned h_hash, other_hash;
+        struct hashmap_entry *e;
+
+        if (!other)
+                return 0;
+
+        assert(h);
+
+        h_hash = h->hash_func(key) % h->n_buckets;
+        if (hash_scan(h, h_hash, key))
+                return -EEXIST;
+
+        other_hash = other->hash_func(key) % other->n_buckets;
+        e = hash_scan(other, other_hash, key);
+        if (!e)
+                return -ENOENT;
+
+        unlink_entry(other, e, other_hash);
+        link_entry(h, e, h_hash);
+
+        return 0;
+}
+
+Hashmap *hashmap_copy(Hashmap *h) {
+        Hashmap *copy;
+
+        assert(h);
+
+        copy = hashmap_new(h->hash_func, h->compare_func);
+        if (!copy)
+                return NULL;
+
+        if (hashmap_merge(copy, h) < 0) {
+                hashmap_free(copy);
+                return NULL;
+        }
+
+        return copy;
+}
+
+char **hashmap_get_strv(Hashmap *h) {
+        char **sv;
+        Iterator it;
+        char *item;
+        int n;
+
+        sv = new(char*, h->n_entries+1);
+        if (!sv)
+                return NULL;
+
+        n = 0;
+        HASHMAP_FOREACH(item, h, it)
+                sv[n++] = item;
+        sv[n] = NULL;
+
+        return sv;
+}
+
+void *hashmap_next(Hashmap *h, const void *key) {
+        unsigned hash;
+        struct hashmap_entry *e;
+
+        assert(h);
+        assert(key);
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % h->n_buckets;
+        e = hash_scan(h, hash, key);
+        if (!e)
+                return NULL;
+
+        e = e->iterate_next;
+        if (!e)
+                return NULL;
+
+        return e->value;
+}
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
new file mode 100644 (file)
index 0000000..d0af8f5
--- /dev/null
@@ -0,0 +1,110 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+/* Pretty straightforward hash table implementation. As a minor
+ * optimization a NULL hashmap object will be treated as empty hashmap
+ * for all read operations. That way it is not necessary to
+ * instantiate an object for each Hashmap use. */
+
+typedef struct Hashmap Hashmap;
+typedef struct _IteratorStruct _IteratorStruct;
+typedef _IteratorStruct* Iterator;
+
+#define ITERATOR_FIRST ((Iterator) 0)
+#define ITERATOR_LAST ((Iterator) -1)
+
+typedef unsigned (*hash_func_t)(const void *p);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+unsigned string_hash_func(const void *p) _pure_;
+int string_compare_func(const void *a, const void *b) _pure_;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+unsigned trivial_hash_func(const void *p) _const_;
+int trivial_compare_func(const void *a, const void *b) _const_;
+
+unsigned uint64_hash_func(const void *p) _pure_;
+int uint64_compare_func(const void *a, const void *b) _pure_;
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
+void hashmap_free(Hashmap *h);
+void hashmap_free_free(Hashmap *h);
+void hashmap_free_free_free(Hashmap *h);
+Hashmap *hashmap_copy(Hashmap *h);
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+int hashmap_update(Hashmap *h, const void *key, void *value);
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+void *hashmap_get(Hashmap *h, const void *key);
+void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
+bool hashmap_contains(Hashmap *h, const void *key);
+void *hashmap_remove(Hashmap *h, const void *key);
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+
+int hashmap_merge(Hashmap *h, Hashmap *other);
+void hashmap_move(Hashmap *h, Hashmap *other);
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
+
+unsigned hashmap_size(Hashmap *h) _pure_;
+bool hashmap_isempty(Hashmap *h) _pure_;
+unsigned hashmap_buckets(Hashmap *h) _pure_;
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
+
+void hashmap_clear(Hashmap *h);
+void hashmap_clear_free(Hashmap *h);
+void hashmap_clear_free_free(Hashmap *h);
+
+void *hashmap_steal_first(Hashmap *h);
+void *hashmap_steal_first_key(Hashmap *h);
+void *hashmap_first(Hashmap *h) _pure_;
+void *hashmap_first_key(Hashmap *h) _pure_;
+void *hashmap_last(Hashmap *h) _pure_;
+
+void *hashmap_next(Hashmap *h, const void *key);
+
+char **hashmap_get_strv(Hashmap *h);
+
+#define HASHMAP_FOREACH(e, h, i) \
+        for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+        for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
+
+#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
+        for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
diff --git a/src/shared/iniparser.c b/src/shared/iniparser.c
new file mode 100644 (file)
index 0000000..55296af
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+   Copyright (c) 2000-2011 by Nicolas Devillard.
+   MIT License
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.c
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <ctype.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ         (1024)
+#define INI_INVALID_KEY     ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                        Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+    LINE_UNPROCESSED,
+    LINE_ERROR,
+    LINE_EMPTY,
+    LINE_COMMENT,
+    LINE_SECTION,
+    LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Convert a string to lowercase.
+  @param    s   String to convert.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string
+  containing a lowercased version of the input string. Do not free
+  or modify the returned string! Since the returned string is statically
+  allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    int i ;
+
+    if (s==NULL) return NULL ;
+    memset(l, 0, ASCIILINESZ+1);
+    i=0 ;
+    while (s[i] && i<ASCIILINESZ) {
+        l[i] = (char)tolower((int)s[i]);
+        i++ ;
+    }
+    l[ASCIILINESZ]=(char)0;
+    return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Remove blanks at the beginning and the end of a string.
+  @param    s   String to parse.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string,
+  which is identical to the input string, except that all blank
+  characters at the end and the beg. of the string have been removed.
+  Do not free or modify the returned string! Since the returned string
+  is statically allocated, it will be modified at each function call
+  (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    char * last ;
+
+    if (s==NULL) return NULL ;
+
+    while (isspace((int)*s) && *s) s++;
+    memset(l, 0, ASCIILINESZ+1);
+    strcpy(l, s);
+    last = l + strlen(l);
+    while (last > l) {
+        if (!isspace((int)*(last-1)))
+            break ;
+        last -- ;
+    }
+    *last = (char)0;
+    return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+    int i ;
+    int nsec ;
+
+    if (d==NULL) return -1 ;
+    nsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            nsec ++ ;
+        }
+    }
+    return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+    int i ;
+    int foundsec ;
+
+    if (d==NULL || n<0) return NULL ;
+    foundsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            foundsec++ ;
+            if (foundsec>n)
+                break ;
+        }
+    }
+    if (foundsec<=n) {
+        return NULL ;
+    }
+    return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+    int     i ;
+
+    if (d==NULL || f==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (d->val[i]!=NULL) {
+            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+        } else {
+            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+        }
+    }
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+    int     i ;
+    int     nsec ;
+    char *  secname ;
+
+    if (d==NULL || f==NULL) return ;
+
+    nsec = iniparser_getnsec(d);
+    if (nsec<1) {
+        /* No section in file: dump all keys as they are */
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+        }
+        return ;
+    }
+    for (i=0 ; i<nsec ; i++) {
+        secname = iniparser_getsecname(d, i) ;
+        iniparser_dumpsection_ini(d, secname, f) ;
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+{
+    int     j ;
+    char    keym[ASCIILINESZ+1];
+    size_t  seclen ;
+
+    if (d==NULL || f==NULL) return ;
+    if (! iniparser_find_entry(d, s)) return ;
+
+    seclen  = strlen(s);
+    fprintf(f, "\n[%s]\n", s);
+    sprintf(keym, "%s:", s);
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            fprintf(f,
+                    "%-30s = %s\n",
+                    d->key[j]+seclen+1,
+                    d->val[j] ? d->val[j] : "");
+        }
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+    size_t  seclen;
+    int     nkeys;
+    char    keym[ASCIILINESZ+1];
+    int j ;
+
+    nkeys = 0;
+
+    if (d==NULL) return nkeys;
+    if (! iniparser_find_entry(d, s)) return nkeys;
+
+    seclen  = strlen(s);
+    sprintf(keym, "%s:", s);
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1))
+            nkeys++;
+    }
+
+    return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+    char **keys;
+
+    int i, j ;
+    char    keym[ASCIILINESZ+1];
+    size_t  seclen;
+    int     nkeys;
+
+    keys = NULL;
+
+    if (d==NULL) return keys;
+    if (! iniparser_find_entry(d, s)) return keys;
+
+    nkeys = iniparser_getsecnkeys(d, s);
+
+    keys = (char**) malloc((size_t)nkeys*sizeof(char*));
+
+    if (!keys)
+           return NULL;
+
+    seclen  = strlen(s);
+    sprintf(keym, "%s:", s);
+
+    i = 0;
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            keys[i] = d->key[j];
+            i++;
+        }
+    }
+
+    return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+    char * lc_key ;
+    char * sval ;
+
+    if (d==NULL || key==NULL)
+        return def ;
+
+    lc_key = strlwc(key);
+    sval = dictionary_get(d, lc_key, def);
+    return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  "42"      ->  42
+  "042"     ->  34 (octal -> decimal)
+  "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+    char    *   c ;
+    int         ret ;
+
+    c = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (c==INI_INVALID_KEY) return notfound ;
+    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+        ret = 1 ;
+    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+        ret = 0 ;
+    } else {
+        ret = notfound ;
+    }
+    return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+    dictionary  *   ini,
+    const char  *   entry
+)
+{
+    int found=0 ;
+    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+        found = 1 ;
+    }
+    return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+    return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+    dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Load a single line from an INI file
+  @param    input_line  Input line, may be concatenated multi-line input
+  @param    section     Output space to store section
+  @param    key         Output space to store key
+  @param    value       Output space to store value
+  @return   line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+    const char * input_line,
+    char * section,
+    char * key,
+    char * value)
+{
+    line_status sta ;
+    char        line[ASCIILINESZ+1];
+    int         len ;
+
+    strcpy(line, strstrip(input_line));
+    len = (int)strlen(line);
+
+    sta = LINE_UNPROCESSED ;
+    if (len<1) {
+        /* Empty line */
+        sta = LINE_EMPTY ;
+    } else if (line[0]=='#' || line[0]==';') {
+        /* Comment line */
+        sta = LINE_COMMENT ;
+    } else if (line[0]=='[' && line[len-1]==']') {
+        /* Section name */
+        sscanf(line, "[%[^]]", section);
+        strcpy(section, strstrip(section));
+        strcpy(section, strlwc(section));
+        sta = LINE_SECTION ;
+    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
+           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
+        /* Usual key=value, with or without comments */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        strcpy(value, strstrip(value));
+        /*
+         * sscanf cannot handle '' or "" as empty values
+         * this is done here
+         */
+        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+            value[0]=0 ;
+        }
+        sta = LINE_VALUE ;
+    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
+        /*
+         * Special cases:
+         * key=
+         * key=;
+         * key=#
+         */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        value[0]=0 ;
+        sta = LINE_VALUE ;
+    } else {
+        /* Generate syntax error */
+        sta = LINE_ERROR ;
+    }
+    return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+    FILE * in ;
+
+    char line    [ASCIILINESZ+1] ;
+    char section [ASCIILINESZ+1] ;
+    char key     [ASCIILINESZ+1] ;
+    char tmp     [ASCIILINESZ+1] ;
+    char val     [ASCIILINESZ+1] ;
+
+    int  last=0 ;
+    int  len ;
+    int  lineno=0 ;
+    int  errs=0;
+
+    dictionary * dict ;
+
+    if ((in=fopen(ininame, "r"))==NULL) {
+        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+        return NULL ;
+    }
+
+    dict = dictionary_new(0) ;
+    if (!dict) {
+        fclose(in);
+        return NULL ;
+    }
+
+    memset(line,    0, ASCIILINESZ);
+    memset(section, 0, ASCIILINESZ);
+    memset(key,     0, ASCIILINESZ);
+    memset(val,     0, ASCIILINESZ);
+    last=0 ;
+
+    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+        lineno++ ;
+        len = (int)strlen(line)-1;
+        if (len==0)
+            continue;
+        /* Safety check against buffer overflows */
+        if (line[len]!='\n' && !feof(in)) {
+            fprintf(stderr,
+                    "iniparser: input line too long in %s (%d)\n",
+                    ininame,
+                    lineno);
+            dictionary_del(dict);
+            fclose(in);
+            return NULL ;
+        }
+        /* Get rid of \n and spaces at end of line */
+        while ((len>=0) &&
+                ((line[len]=='\n') || (isspace(line[len])))) {
+            line[len]=0 ;
+            len-- ;
+        }
+        /* Detect multi-line */
+        if (line[len]=='\\') {
+            /* Multi-line value */
+            last=len ;
+            continue ;
+        } else {
+            last=0 ;
+        }
+        switch (iniparser_line(line, section, key, val)) {
+            case LINE_EMPTY:
+            case LINE_COMMENT:
+            break ;
+
+            case LINE_SECTION:
+            errs = dictionary_set(dict, section, NULL);
+            break ;
+
+            case LINE_VALUE:
+            sprintf(tmp, "%s:%s", section, key);
+            errs = dictionary_set(dict, tmp, val) ;
+            break ;
+
+            case LINE_ERROR:
+            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+                    ininame,
+                    lineno);
+            fprintf(stderr, "-> %s\n", line);
+            errs++ ;
+            break;
+
+            default:
+            break ;
+        }
+        memset(line, 0, ASCIILINESZ);
+        last=0;
+        if (errs<0) {
+            fprintf(stderr, "iniparser: memory allocation failure\n");
+            break ;
+        }
+    }
+    if (errs) {
+        dictionary_del(dict);
+        dict = NULL ;
+    }
+    fclose(in);
+    return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+    dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/src/shared/iniparser.h b/src/shared/iniparser.h
new file mode 100644 (file)
index 0000000..62d6de9
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+   Copyright (c) 2000-2011 by Nicolas Devillard.
+   MIT License
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.h
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  - "42"      ->  42
+  - "042"     ->  34 (octal -> decimal)
+  - "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
diff --git a/src/shared/list.h b/src/shared/list.h
new file mode 100644 (file)
index 0000000..4a6843d
--- /dev/null
@@ -0,0 +1,136 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define LIST_HEAD(t,name)                                               \
+        t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define LIST_FIELDS(t,name)                                             \
+        t *name##_next, *name##_prev
+
+/* Initialize the list's head */
+#define LIST_HEAD_INIT(t,head)                                          \
+        do {                                                            \
+                (head) = NULL; }                                        \
+        while(false)
+
+/* Initialize a list item */
+#define LIST_INIT(t,name,item)                                          \
+        do {                                                            \
+                t *_item = (item);                                      \
+                assert(_item);                                          \
+                _item->name##_prev = _item->name##_next = NULL;         \
+        } while(false)
+
+/* Prepend an item to the list */
+#define LIST_PREPEND(t,name,head,item)                                  \
+        do {                                                            \
+                t **_head = &(head), *_item = (item);                   \
+                assert(_item);                                          \
+                if ((_item->name##_next = *_head))                      \
+                        _item->name##_next->name##_prev = _item;        \
+                _item->name##_prev = NULL;                              \
+                *_head = _item;                                         \
+        } while(false)
+
+/* Remove an item from the list */
+#define LIST_REMOVE(t,name,head,item)                                   \
+        do {                                                            \
+                t **_head = &(head), *_item = (item);                   \
+                assert(_item);                                          \
+                if (_item->name##_next)                                 \
+                        _item->name##_next->name##_prev = _item->name##_prev; \
+                if (_item->name##_prev)                                 \
+                        _item->name##_prev->name##_next = _item->name##_next; \
+                else {                                                  \
+                        assert(*_head == _item);                        \
+                        *_head = _item->name##_next;                    \
+                }                                                       \
+                _item->name##_next = _item->name##_prev = NULL;         \
+        } while(false)
+
+/* Find the head of the list */
+#define LIST_FIND_HEAD(t,name,item,head)                                \
+        do {                                                            \
+                t *_item = (item);                                      \
+                assert(_item);                                          \
+                while (_item->name##_prev)                              \
+                       _item = _item->name##_prev;                      \
+                (head) = _item;                                         \
+        } while (false)
+
+/* Find the tail of the list */
+#define LIST_FIND_TAIL(t,name,item,tail)                                \
+        do {                                                            \
+                t *_item = (item);                                      \
+                assert(_item);                                          \
+                while (_item->name##_next)                              \
+                        _item = _item->name##_next;                     \
+                (tail) = _item;                                         \
+        } while (false)
+
+/* Insert an item after another one (a = where, b = what) */
+#define LIST_INSERT_AFTER(t,name,head,a,b)                              \
+        do {                                                            \
+                t **_head = &(head), *_a = (a), *_b = (b);              \
+                assert(_b);                                             \
+                if (!_a) {                                              \
+                        if ((_b->name##_next = *_head))                 \
+                                _b->name##_next->name##_prev = _b;      \
+                        _b->name##_prev = NULL;                         \
+                        *_head = _b;                                    \
+                } else {                                                \
+                        if ((_b->name##_next = _a->name##_next))        \
+                                _b->name##_next->name##_prev = _b;      \
+                        _b->name##_prev = _a;                           \
+                        _a->name##_next = _b;                           \
+                }                                                       \
+        } while(false)
+
+#define LIST_JUST_US(name,item)                                         \
+        (!(item)->name##_prev && !(item)->name##_next)                  \
+
+#define LIST_FOREACH(name,i,head)                                       \
+        for ((i) = (head); (i); (i) = (i)->name##_next)
+
+#define LIST_FOREACH_SAFE(name,i,n,head)                                \
+        for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
+
+#define LIST_FOREACH_BEFORE(name,i,p)                                   \
+        for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
+
+#define LIST_FOREACH_AFTER(name,i,p)                                    \
+        for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+
+/* Loop starting from p->next until p->prev.
+   p can be adjusted meanwhile. */
+#define LIST_LOOP_BUT_ONE(name,i,head,p)                                \
+        for ((i) = (p)->name##_next ? (p)->name##_next : (head);        \
+             (i) != (p);                                                \
+             (i) = (i)->name##_next ? (i)->name##_next : (head))
diff --git a/src/shared/log.c b/src/shared/log.c
new file mode 100644 (file)
index 0000000..d3ae03c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+void buxton_log(const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vfprintf(stderr, fmt, args);
+       va_end(args);
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/log.h b/src/shared/log.h
new file mode 100644 (file)
index 0000000..080acdb
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#ifdef DEBUG
+#define buxton_debug(...) do { \
+       (buxton_log("%s():[%d]: ",  __func__, __LINE__), buxton_log(__VA_ARGS__)); \
+} while(0);
+#else
+#define buxton_debug(...) do {} while(0);
+#endif /* DEBUG */
+
+void buxton_log(const char *fmt, ...);
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/macro.h b/src/shared/macro.h
new file mode 100644 (file)
index 0000000..c4abb0b
--- /dev/null
@@ -0,0 +1,83 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+#define _sentinel_ __attribute__ ((sentinel))
+#define _noreturn_ __attribute__((noreturn))
+#define _unused_ __attribute__ ((unused))
+#define _destructor_ __attribute__ ((destructor))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _deprecated_ __attribute__ ((deprecated))
+#define _packed_ __attribute__ ((packed))
+#define _malloc_ __attribute__ ((malloc))
+#define _weak_ __attribute__ ((weak))
+#define _likely_(x) (__builtin_expect(!!(x),1))
+#define _unlikely_(x) (__builtin_expect(!!(x),0))
+#define _public_ __attribute__ ((visibility("default")))
+#define _hidden_ __attribute__ ((visibility("hidden")))
+#define _weakref_(x) __attribute__((weakref(#x)))
+#define _introspect_(x) __attribute__((section("introspect." x)))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* Rounds up */
+
+#define ALIGN4(l) (((l) + 3) & (unsigned)~3)
+#define ALIGN8(l) (((l) + 7) & (unsigned)~7)
+
+#if __SIZEOF_POINTER__ == 8
+#define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+#define ALIGN(l) ALIGN4(l)
+#else
+#error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p))
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        return ((l + ali - 1) & (unsigned)~(ali - 1));
+}
+
+#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define memzero(x,l) (memset((x), 0, (l)))
diff --git a/src/shared/protocol.c b/src/shared/protocol.c
new file mode 100644 (file)
index 0000000..fdf73c4
--- /dev/null
@@ -0,0 +1,937 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "buxtonclient.h"
+#include "buxtonkey.h"
+#include "buxtonresponse.h"
+#include "buxtonstring.h"
+#include "hashmap.h"
+#include "log.h"
+#include "protocol.h"
+#include "util.h"
+
+#define TIMEOUT 3
+
+static pthread_mutex_t callback_guard = PTHREAD_MUTEX_INITIALIZER;
+static Hashmap *callbacks = NULL;
+static Hashmap *notify_callbacks = NULL;
+static volatile uint32_t _msgid = 0;
+
+struct notify_value {
+       void *data;
+       BuxtonCallback cb;
+       struct timeval tv;
+       BuxtonControlMessage type;
+       _BuxtonKey *key;
+};
+
+static uint32_t get_msgid(void)
+{
+       return __sync_fetch_and_add(&_msgid, 1);
+}
+
+bool setup_callbacks(void)
+{
+       bool r = false;
+       int s;
+
+       s = pthread_mutex_lock(&callback_guard);
+       if (s) {
+               return false;
+       }
+
+       if (callbacks && notify_callbacks) {
+               goto unlock;
+       }
+
+       callbacks = hashmap_new(trivial_hash_func, trivial_compare_func);
+       if (!callbacks) {
+               goto unlock;
+       }
+
+       notify_callbacks = hashmap_new(trivial_hash_func, trivial_compare_func);
+       if (!notify_callbacks) {
+               hashmap_free(callbacks);
+               goto unlock;
+       }
+
+       r = true;
+
+unlock:
+       s = pthread_mutex_unlock(&callback_guard);
+       if (s) {
+               return false;
+       }
+
+       return r;
+}
+
+void cleanup_callbacks(void)
+{
+       struct notify_value *nvi;
+       Iterator it;
+#if UINTPTR_MAX == 0xffffffffffffffff
+       uint64_t hkey;
+#else
+       uint32_t hkey;
+#endif
+
+       (void)pthread_mutex_lock(&callback_guard);
+
+       if (callbacks) {
+               HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) {
+                       (void)hashmap_remove(callbacks, (void *)hkey);
+                       key_free(nvi->key);
+                       free(nvi);
+               }
+               hashmap_free(callbacks);
+       }
+       callbacks = NULL;
+
+       if (notify_callbacks) {
+               HASHMAP_FOREACH_KEY(nvi, hkey, notify_callbacks, it) {
+                       (void)hashmap_remove(notify_callbacks, (void *)hkey);
+                       key_free(nvi->key);
+                       free(nvi);
+               }
+               hashmap_free(notify_callbacks);
+       }
+       notify_callbacks = NULL;
+
+       (void)pthread_mutex_unlock(&callback_guard);
+}
+
+void run_callback(BuxtonCallback callback, void *data, size_t count,
+                 BuxtonData *list, BuxtonControlMessage type, _BuxtonKey *key)
+{
+       BuxtonArray *array = NULL;
+       _BuxtonResponse response;
+
+       if (!callback) {
+               goto out;
+       }
+
+       array = buxton_array_new();
+       if (!array) {
+               goto out;
+       }
+
+       for (int i = 0; i < count; i++)
+               if (!buxton_array_add(array, &list[i])) {
+                       goto out;
+               }
+
+       response.type = type;
+       response.data = array;
+       response.key = key;
+       callback(&response, data);
+
+out:
+       buxton_array_free(&array, NULL);
+}
+
+void reap_callbacks(void)
+{
+       struct notify_value *nvi;
+       struct timeval tv;
+       Iterator it;
+#if UINTPTR_MAX == 0xffffffffffffffff
+       uint64_t hkey;
+#else
+       uint32_t hkey;
+#endif
+
+       (void)gettimeofday(&tv, NULL);
+
+       /* remove timed out callbacks */
+       HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) {
+               if (tv.tv_sec - nvi->tv.tv_sec > TIMEOUT) {
+                       (void)hashmap_remove(callbacks, (void *)hkey);
+                       key_free(nvi->key);
+                       free(nvi);
+               }
+       }
+}
+
+bool send_message(_BuxtonClient *client, uint8_t *send, size_t send_len,
+                 BuxtonCallback callback, void *data, uint32_t msgid,
+                 BuxtonControlMessage type, _BuxtonKey *key)
+{
+       struct notify_value *nv;
+       _BuxtonKey *k = NULL;
+       int s;
+       bool r = false;
+
+       nv = malloc0(sizeof(struct notify_value));
+       if (!nv) {
+               goto fail;
+       }
+
+       if (key) {
+               k = malloc0(sizeof(_BuxtonKey));
+               if (!k) {
+                       goto fail;
+               }
+               if (!buxton_key_copy(key, k)) {
+                       goto fail;
+               }
+       }
+
+       (void)gettimeofday(&nv->tv, NULL);
+       nv->cb = callback;
+       nv->data = data;
+       nv->type = type;
+       nv->key = k;
+
+       s = pthread_mutex_lock(&callback_guard);
+       if (s) {
+               goto fail;
+       }
+
+       reap_callbacks();
+
+#if UINTPTR_MAX == 0xffffffffffffffff
+       s = hashmap_put(callbacks, (void *)((uint64_t)msgid), nv);
+#else
+       s = hashmap_put(callbacks, (void *)msgid, nv);
+#endif
+       (void)pthread_mutex_unlock(&callback_guard);
+
+       if (s < 1) {
+               buxton_debug("Error adding callback for msgid: %llu\n", msgid);
+               goto fail;
+       }
+
+       /* Now write it off */
+       if (!_write(client->fd, send, send_len)) {
+               buxton_debug("Write failed for msgid: %llu\n", msgid);
+               r = false;
+       } else {
+               r = true;
+       }
+
+       return r;
+
+fail:
+       free(nv);
+       key_free(k);
+       return false;
+}
+
+void handle_callback_response(BuxtonControlMessage msg, uint32_t msgid,
+                             BuxtonData *list, size_t count)
+{
+       struct notify_value *nv;
+
+       /* use notification callbacks for notification messages */
+       if (msg == BUXTON_CONTROL_CHANGED) {
+#if UINTPTR_MAX == 0xffffffffffffffff
+               nv = hashmap_get(notify_callbacks, (void *)((uint64_t)msgid));
+#else
+               nv = hashmap_get(notify_callbacks, (void *)msgid);
+#endif
+               if (!nv) {
+                       return;
+               }
+
+               run_callback((BuxtonCallback)(nv->cb), nv->data, count, list,
+                            BUXTON_CONTROL_CHANGED, nv->key);
+               return;
+       }
+
+#if UINTPTR_MAX == 0xffffffffffffffff
+       nv = hashmap_remove(callbacks, (void *)((uint64_t)msgid));
+#else
+       nv = hashmap_remove(callbacks, (void *)msgid);
+#endif
+       if (!nv) {
+               return;
+       }
+
+       if (nv->type == BUXTON_CONTROL_NOTIFY) {
+               if (list[0].type == INT32 &&
+                   list[0].store.d_int32 == 0) {
+#if UINTPTR_MAX == 0xffffffffffffffff
+                       if (hashmap_put(notify_callbacks, (void *)((uint64_t)msgid), nv)
+#else
+                       if (hashmap_put(notify_callbacks, (void *)msgid, nv)
+#endif
+                           >= 0) {
+                               return;
+                       }
+               }
+       } else if (nv->type == BUXTON_CONTROL_UNNOTIFY) {
+               if (list[0].type == INT32 &&
+                   list[0].store.d_int32 == 0) {
+                       (void)hashmap_remove(notify_callbacks,
+#if UINTPTR_MAX == 0xffffffffffffffff
+                                            (void *)((uint64_t)list[2].store.d_uint32));
+#else
+                                            (void *)list[2].store.d_uint32);
+#endif
+
+                       return;
+               }
+       }
+
+       /* callback should be run on notfiy or unnotify failure */
+       /* and on any other server message we are waiting for */
+       run_callback((BuxtonCallback)(nv->cb), nv->data, count, list, nv->type,
+                    nv->key);
+
+       key_free(nv->key);
+       free(nv);
+}
+
+ssize_t buxton_wire_handle_response(_BuxtonClient *client)
+{
+       ssize_t l;
+       _cleanup_free_ uint8_t *response = NULL;
+       BuxtonData *r_list = NULL;
+       BuxtonControlMessage r_msg = BUXTON_CONTROL_MIN;
+       ssize_t count = 0;
+       size_t offset = 0;
+       size_t size = BUXTON_MESSAGE_HEADER_LENGTH;
+       uint32_t r_msgid;
+       int s;
+       ssize_t handled = 0;
+
+       s = pthread_mutex_lock(&callback_guard);
+       if (s) {
+               return 0;
+       }
+       reap_callbacks();
+       (void)pthread_mutex_unlock(&callback_guard);
+
+       response = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
+       if (!response) {
+               return 0;
+       }
+
+       do {
+               l = read(client->fd, response + offset, size - offset);
+               if (l <= 0) {
+                       return handled;
+               }
+               offset += (size_t)l;
+               if (offset < BUXTON_MESSAGE_HEADER_LENGTH) {
+                       continue;
+               }
+               if (size == BUXTON_MESSAGE_HEADER_LENGTH) {
+                       size = buxton_get_message_size(response, offset);
+                       if (size == 0 || size > BUXTON_MESSAGE_MAX_LENGTH) {
+                               return -1;
+                       }
+               }
+               if (size != BUXTON_MESSAGE_HEADER_LENGTH) {
+                       response = realloc(response, size);
+                       if (!response) {
+                               return -1;
+                       }
+               }
+               if (size != offset) {
+                       continue;
+               }
+
+               count = buxton_deserialize_message(response, &r_msg, size, &r_msgid, &r_list);
+               if (count < 0) {
+                       goto next;
+               }
+
+               if (!(r_msg == BUXTON_CONTROL_STATUS && r_list && r_list[0].type == INT32)
+                   && !(r_msg == BUXTON_CONTROL_CHANGED)) {
+                       handled++;
+                       buxton_log("Critical error: Invalid response\n");
+                       goto next;
+               }
+
+               s = pthread_mutex_lock(&callback_guard);
+               if (s) {
+                       goto next;
+               }
+
+               handle_callback_response(r_msg, r_msgid, r_list, (size_t)count);
+
+               (void)pthread_mutex_unlock(&callback_guard);
+               handled++;
+
+       next:
+               if (r_list) {
+                       for (int i = 0; i < count; i++) {
+                               if (r_list[i].type == STRING) {
+                                       free(r_list[i].store.d_string.value);
+                               }
+                       }
+                       free(r_list);
+               }
+
+               /* reset for next possible message */
+               size = BUXTON_MESSAGE_HEADER_LENGTH;
+               offset = 0;
+       } while (true);
+}
+
+int buxton_wire_get_response(_BuxtonClient *client)
+{
+       struct pollfd pfd[1];
+       int r;
+       ssize_t processed;
+
+       pfd[0].fd = client->fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       r = poll(pfd, 1, 5000);
+
+       if (r == 0) {
+               return -ETIME;
+       } else if (r < 0) {
+               return -errno;
+       }
+
+       processed = buxton_wire_handle_response(client);
+
+       return (int)processed;
+}
+
+bool buxton_wire_set_value(_BuxtonClient *client, _BuxtonKey *key, void *value,
+                          BuxtonCallback callback, void *data)
+{
+       _cleanup_free_ uint8_t *send = NULL;
+       bool ret = false;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_value;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->layer, &d_layer);
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(&key->name, &d_name);
+       d_value.type = key->type;
+       switch (key->type) {
+       case STRING:
+               d_value.store.d_string.value = (char *)value;
+               d_value.store.d_string.length = (uint32_t)strlen((char *)value) + 1;
+               break;
+       case INT32:
+               d_value.store.d_int32 = *(int32_t *)value;
+               break;
+       case INT64:
+               d_value.store.d_int64 = *(int64_t *)value;
+               break;
+       case UINT32:
+               d_value.store.d_uint32 = *(uint32_t *)value;
+               break;
+       case UINT64:
+               d_value.store.d_uint64 = *(uint64_t *)value;
+               break;
+       case FLOAT:
+               d_value.store.d_float = *(float *)value;
+               break;
+       case DOUBLE:
+               memcpy(&d_value.store.d_double, value, sizeof(double));
+               break;
+       case BOOLEAN:
+               d_value.store.d_boolean = *(bool *)value;
+               break;
+       default:
+               break;
+       }
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Failed to add layer to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_name)) {
+               buxton_log("Failed to add name to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_value)) {
+               buxton_log("Failed to add value to set_value array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET, msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_SET, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_set_label(_BuxtonClient *client,
+                          _BuxtonKey *key, BuxtonString *value,
+                          BuxtonCallback callback, void *data)
+{
+       assert(client);
+       assert(key);
+       assert(value);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       bool ret = false;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_value;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->layer, &d_layer);
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(value, &d_value);
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Failed to add layer to set_label array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_label array\n");
+               goto end;
+       }
+       if (key->name.value) {
+               buxton_string_to_data(&key->name, &d_name);
+               if (!buxton_array_add(list, &d_name)) {
+                       buxton_log("Failed to add name to set_label array\n");
+                       goto end;
+               }
+       }
+       if (!buxton_array_add(list, &d_value)) {
+               buxton_log("Failed to add value to set_label array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET_LABEL, msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_SET_LABEL, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_create_group(_BuxtonClient *client, _BuxtonKey *key,
+                             BuxtonCallback callback, void *data)
+{
+       assert(client);
+       assert(key);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       bool ret = false;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       BuxtonData d_group;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->layer, &d_layer);
+       buxton_string_to_data(&key->group, &d_group);
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Failed to add layer to create_group array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to create_group array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_CREATE_GROUP, msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_CREATE_GROUP, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key,
+                             BuxtonCallback callback, void *data)
+{
+       assert(client);
+       assert(key);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       bool ret = false;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       BuxtonData d_group;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->layer, &d_layer);
+       buxton_string_to_data(&key->group, &d_group);
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Failed to add layer to remove_group array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to remove_group array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_REMOVE_GROUP, msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_REMOVE_GROUP, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key,
+                          BuxtonCallback callback, void *data)
+{
+       bool ret = false;
+       size_t send_len = 0;
+       _cleanup_free_ uint8_t *send = NULL;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_type;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(&key->name, &d_name);
+       d_type.type = UINT32;
+       d_type.store.d_int32 = key->type;
+
+       list = buxton_array_new();
+       if (key->layer.value) {
+               buxton_string_to_data(&key->layer, &d_layer);
+               if (!buxton_array_add(list, &d_layer)) {
+                       buxton_log("Unable to prepare get_value message\n");
+                       goto end;
+               }
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_name)) {
+               buxton_log("Failed to add name to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_type)) {
+               buxton_log("Failed to add type to set_value array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_GET, msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_GET, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_unset_value(_BuxtonClient *client,
+                            _BuxtonKey *key,
+                            BuxtonCallback callback,
+                            void *data)
+{
+       assert(client);
+       assert(key);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_layer;
+       BuxtonData d_type;
+       bool ret = false;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(&key->name, &d_name);
+       buxton_string_to_data(&key->layer, &d_layer);
+       d_type.type = UINT32;
+       d_type.store.d_int32 = key->type;
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Failed to add layer to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_name)) {
+               buxton_log("Failed to add name to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_type)) {
+               buxton_log("Failed to add type to set_value array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNSET,
+                                           msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_UNSET, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_list_keys(_BuxtonClient *client,
+                          BuxtonString *layer,
+                          BuxtonCallback callback,
+                          void *data)
+{
+       assert(client);
+       assert(layer);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_layer;
+       bool ret = false;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(layer, &d_layer);
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_layer)) {
+               buxton_log("Unable to add layer to list_keys array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_LIST, msgid,
+                                           list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_LIST, NULL)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+
+       return ret;
+}
+
+bool buxton_wire_register_notification(_BuxtonClient *client,
+                                      _BuxtonKey *key,
+                                      BuxtonCallback callback,
+                                      void *data)
+{
+       assert(client);
+       assert(key);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_type;
+       bool ret = false;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(&key->name, &d_name);
+       d_type.type = UINT32;
+       d_type.store.d_int32 = key->type;
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_name)) {
+               buxton_log("Failed to add name to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_type)) {
+               buxton_log("Failed to add type to set_value array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_NOTIFY, msgid,
+                                           list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_NOTIFY, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+bool buxton_wire_unregister_notification(_BuxtonClient *client,
+                                        _BuxtonKey *key,
+                                        BuxtonCallback callback,
+                                        void *data)
+{
+       assert(client);
+       assert(key);
+
+       _cleanup_free_ uint8_t *send = NULL;
+       size_t send_len = 0;
+       BuxtonArray *list = NULL;
+       BuxtonData d_group;
+       BuxtonData d_name;
+       BuxtonData d_type;
+       bool ret = false;
+       uint32_t msgid = get_msgid();
+
+       buxton_string_to_data(&key->group, &d_group);
+       buxton_string_to_data(&key->name, &d_name);
+       d_type.type = UINT32;
+       d_type.store.d_int32 = key->type;
+
+       list = buxton_array_new();
+       if (!buxton_array_add(list, &d_group)) {
+               buxton_log("Failed to add group to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_name)) {
+               buxton_log("Failed to add name to set_value array\n");
+               goto end;
+       }
+       if (!buxton_array_add(list, &d_type)) {
+               buxton_log("Failed to add type to set_value array\n");
+               goto end;
+       }
+
+       send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNNOTIFY,
+                                           msgid, list);
+
+       if (send_len == 0) {
+               goto end;
+       }
+
+       if (!send_message(client, send, send_len, callback, data, msgid,
+                         BUXTON_CONTROL_UNNOTIFY, key)) {
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       buxton_array_free(&list, NULL);
+       return ret;
+}
+
+void include_protocol(void)
+{
+       ;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/protocol.h b/src/shared/protocol.h
new file mode 100644 (file)
index 0000000..b3e789e
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file protocol.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used for the wire protocol
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include "buxton.h"
+#include "buxtonclient.h"
+#include "buxtonkey.h"
+#include "list.h"
+#include "serialize.h"
+#include "hashmap.h"
+
+/**
+ * Initialize callback hashamps
+ * @return a boolean value, indicating success of the operation
+ */
+bool setup_callbacks(void)
+       __attribute__((warn_unused_result));
+
+/**
+ * free callback hashmaps
+ */
+void cleanup_callbacks(void);
+
+/**
+ * Execute callback function on list using user data
+ * @param callback User callback function executed
+ * @param data User data passed to callback function
+ * @param count number of elements in list
+ * @param list Data from buxtond
+ * @param type Message type of the callback
+ * @param key Key used to make the request
+ */
+void run_callback(BuxtonCallback callback, void *data, size_t count,
+                 BuxtonData *list, BuxtonControlMessage type,
+                 _BuxtonKey *key);
+
+/**
+ * cleanup expired messages (must hold callback_guard lock)
+ */
+void reap_callbacks(void);
+
+/**
+ * Write message to buxtond
+ * @param client Client connection
+ * @param send serialized data to send to buxtond
+ * @param send_len size of send
+ * @param callback Callback function used to handle buxtond's response
+ * @param data User data passed to callback function
+ * @param msgid Message id used to identify buxtond's response
+ * @param type The type of request being sent to buxtond
+ * @return a boolean value, indicating success of the operation
+ */
+bool send_message(_BuxtonClient *client, uint8_t *send, size_t send_len,
+                 BuxtonCallback callback, void *data, uint32_t msgid,
+                 BuxtonControlMessage type, _BuxtonKey *key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Check for callbacks for daemon's response
+ * @param msg Buxton message type
+ * @param msgid Key for message lookup
+ * @param list array of BuxtonData
+ * @param count number of elements in list
+ */
+void handle_callback_response(BuxtonControlMessage msg, uint32_t msgid,
+                             BuxtonData *list, size_t count);
+
+/**
+ * Parse responses from buxtond and run callbacks on received messages
+ * @param client A BuxtonClient
+ * @return number of received messages processed
+ */
+
+ssize_t buxton_wire_handle_response(_BuxtonClient *client)
+       __attribute__((warn_unused_result));
+
+/**
+ * Wait for a response from buxtond and then call handle response
+ * @param client Client connection
+ * @return an int value, indicating success of the operation
+ */
+int buxton_wire_get_response(_BuxtonClient *client);
+
+/**
+ * Send a SET message over the wire protocol, return the response
+ * @param client Client connection
+ * @param key _BuxtonKey pointer
+ * @param value A pointer to a new value
+ * @param data A BuxtonData storing the new value
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_set_value(_BuxtonClient *client, _BuxtonKey *key, void *value,
+                          BuxtonCallback callback, void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send a SET_LABEL message over the wire protocol, return the response
+ *
+ * @note This is a privileged operation, so it will return false for unprivileged clients
+ *
+ * @param client Client connection
+ * @param key Key or group name
+ * @param value Key or group label
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_set_label(_BuxtonClient *client, _BuxtonKey *key,
+                          BuxtonString *value, BuxtonCallback callback,
+                          void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send a CREATE_GROUP message over the wire protocol, return the response
+ *
+ * @note This is a privileged operation, so it will return false for unprivileged clients
+ *
+ * @param client Client connection
+ * @param key Key with group and layer members initialized
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_create_group(_BuxtonClient *client, _BuxtonKey *key,
+                             BuxtonCallback callback, void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send a REMOVE_GROUP message over the wire protocol, return the response
+ *
+ * @note This is a privileged operation, so it will return false for unprivileged clients
+ *
+ * @param client Client connection
+ * @param key Key with group and layer members initialized
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key,
+                             BuxtonCallback callback, void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send a GET message over the wire protocol, return the data
+ * @param client Client connection
+ * @param key _BuxtonKey pointer
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback functionb
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key,
+                          BuxtonCallback callback, void *data)
+       __attribute__((warn_unused_result));
+
+
+/**
+ * Send an UNSET message over the wire protocol, return the response
+ * @param client Client connection
+ * @param key _BuxtonKey pointer
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_unset_value(_BuxtonClient *client,
+                            _BuxtonKey *key,
+                            BuxtonCallback callback,
+                            void *data)
+       __attribute__((warn_unused_result));
+/**
+ * Send a NOTIFY message over the protocol, register for events
+ * @param client Client connection
+ * @param key _BuxtonKey pointer
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_register_notification(_BuxtonClient *client,
+                                      _BuxtonKey *key,
+                                      BuxtonCallback callback,
+                                      void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send a LIST message over the protocol, retrieve key list
+ * @param client Client connection
+ * @param layer Layer name
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_list_keys(_BuxtonClient *client,
+                          BuxtonString *layer,
+                          BuxtonCallback callback,
+                          void *data)
+       __attribute__((warn_unused_result));
+
+/**
+ * Send an UNNOTIFY message over the protocol, no longer recieve events
+ * @param client Client connection
+ * @param key _BuxtonKey pointer
+ * @param callback A callback function to handle daemon reply
+ * @param data User data to be used with callback function
+ * @return a boolean value, indicating success of the operation
+ */
+bool buxton_wire_unregister_notification(_BuxtonClient *client,
+                                        _BuxtonKey *key,
+                                        BuxtonCallback callback,
+                                        void *data)
+       __attribute__((warn_unused_result));
+
+void include_protocol(void);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/serialize.c b/src/shared/serialize.c
new file mode 100644 (file)
index 0000000..86d3a7d
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <errno.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buxton.h"
+#include "log.h"
+#include "serialize.h"
+#include "smack.h"
+#include "util.h"
+
+
+size_t buxton_serialize(BuxtonData *source, BuxtonString *label,
+                       uint8_t **target)
+{
+       size_t length;
+       size_t size;
+       size_t offset = 0;
+       uint8_t *data = NULL;
+       size_t ret = 0;
+
+       assert(source);
+       assert(target);
+
+       /* DataType + length field */
+       size = sizeof(BuxtonDataType) + (sizeof(uint32_t) * 2) + label->length;
+
+       /* Total size will be different for string data */
+       switch (source->type) {
+       case STRING:
+               length = source->store.d_string.length;
+               break;
+       default:
+               length = sizeof(source->store);
+               break;
+       }
+
+       size += length;
+
+       /* Allocate memory big enough to hold all information */
+       data = malloc0(size);
+       if (!data) {
+               abort();
+       }
+
+       /* Write the entire BuxtonDataType to the first block */
+       memcpy(data, &(source->type), sizeof(BuxtonDataType));
+       offset += sizeof(BuxtonDataType);
+
+       /* Write out the length of the label field */
+       memcpy(data+offset, &(label->length), sizeof(uint32_t));
+       offset += sizeof(uint32_t);
+
+       /* Write out the length of the data field */
+       memcpy(data+offset, &length, sizeof(uint32_t));
+       offset += sizeof(uint32_t);
+
+       /* Write out the label field */
+       memcpy(data+offset, label->value, label->length);
+       offset += label->length;
+
+       /* Write the data itself */
+       switch (source->type) {
+       case STRING:
+               memcpy(data+offset, source->store.d_string.value, length);
+               break;
+       case INT32:
+               memcpy(data+offset, &(source->store.d_int32), sizeof(int32_t));
+               break;
+       case UINT32:
+               memcpy(data+offset, &(source->store.d_uint32), sizeof(uint32_t));
+               break;
+       case INT64:
+               memcpy(data+offset, &(source->store.d_int64), sizeof(int64_t));
+               break;
+       case UINT64:
+               memcpy(data+offset, &(source->store.d_uint64), sizeof(uint64_t));
+               break;
+       case FLOAT:
+               memcpy(data+offset, &(source->store.d_float), sizeof(float));
+               break;
+       case DOUBLE:
+               memcpy(data+offset, &(source->store.d_double), sizeof(double));
+               break;
+       case BOOLEAN:
+               memcpy(data+offset, &(source->store.d_boolean), sizeof(bool));
+               break;
+       default:
+               abort();
+       }
+
+       ret = size;
+       *target = data;
+
+       assert(ret >= BXT_MINIMUM_SIZE);
+
+       return ret;
+}
+
+void buxton_deserialize(uint8_t *source, BuxtonData *target,
+                       BuxtonString *label)
+{
+       size_t offset = 0;
+       size_t length = 0;
+       BuxtonDataType type;
+
+       assert(source);
+       assert(target);
+       assert(label);
+
+       /* Retrieve the BuxtonDataType */
+       type = *(BuxtonDataType*)source;
+       offset += sizeof(BuxtonDataType);
+
+       /* Retrieve the length of the label */
+       label->length = *(uint32_t*)(source+offset);
+       offset += sizeof(uint32_t);
+
+       /* Retrieve the length of the value */
+       length = *(uint32_t*)(source+offset);
+       offset += sizeof(uint32_t);
+
+       /* Retrieve the label */
+       label->value = malloc(label->length);
+       if (label->length > 0 && !label->value) {
+               abort();
+       }
+       memcpy(label->value, source+offset, label->length);
+       offset += label->length;
+
+       switch (type) {
+       case STRING:
+               /* User must free the string */
+               target->store.d_string.value = malloc(length);
+               if (!target->store.d_string.value) {
+                       abort();
+               }
+               memcpy(target->store.d_string.value, source+offset, length);
+               target->store.d_string.length = (uint32_t)length;
+               break;
+       case INT32:
+               target->store.d_int32 = *(int32_t*)(source+offset);
+               break;
+       case UINT32:
+               target->store.d_uint32 = *(uint32_t*)(source+offset);
+               break;
+       case INT64:
+               target->store.d_int64 = *(int64_t*)(source+offset);
+               break;
+       case UINT64:
+               target->store.d_uint64 = *(uint64_t*)(source+offset);
+               break;
+       case FLOAT:
+               target->store.d_float = *(float*)(source+offset);
+               break;
+       case DOUBLE:
+               memcpy(&target->store.d_double, source + offset, sizeof(double));
+               break;
+       case BOOLEAN:
+               target->store.d_boolean = *(bool*)(source+offset);
+               break;
+       default:
+               buxton_debug("Invalid BuxtonDataType: %lu\n", type);
+               abort();
+       }
+
+       /* Successful */
+       target->type = type;
+}
+
+size_t buxton_serialize_message(uint8_t **dest, BuxtonControlMessage message,
+                               uint32_t msgid, BuxtonArray *list)
+{
+       uint16_t i = 0;
+       uint8_t *data = NULL;
+       size_t ret = 0;
+       size_t offset = 0;
+       size_t size = 0;
+       size_t curSize = 0;
+       uint16_t control, msg;
+
+       assert(dest);
+
+       buxton_debug("Serializing message...\n");
+
+       if (list->len > BUXTON_MESSAGE_MAX_PARAMS) {
+               errno = EINVAL;
+               return ret;
+       }
+
+       if (message >= BUXTON_CONTROL_MAX || message < BUXTON_CONTROL_SET) {
+               errno = EINVAL;
+               return ret;
+       }
+
+       /*
+        * initial size =
+        * control code + control message (uint16_t * 2) +
+        * message size (uint32_t) +
+        * message id (uint32_t) +
+        * param count (uint32_t)
+        */
+       data = malloc0(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) +
+                      sizeof(uint32_t));
+       if (!data) {
+               errno = ENOMEM;
+               goto end;
+       }
+
+       control = BUXTON_CONTROL_CODE;
+       memcpy(data, &control, sizeof(uint16_t));
+       offset += sizeof(uint16_t);
+
+       msg = (uint16_t)message;
+       memcpy(data+offset, &msg, sizeof(uint16_t));
+       offset += sizeof(uint16_t);
+
+       /* Save room for final size */
+       offset += sizeof(uint32_t);
+
+       memcpy(data+offset, &msgid, sizeof(uint32_t));
+       offset += sizeof(uint32_t);
+
+       /* Now write the parameter count */
+       memcpy(data+offset, &(list->len), sizeof(uint32_t));
+       offset += sizeof(uint32_t);
+
+       size = offset;
+
+       /* Deal with parameters */
+       BuxtonData *param;
+       size_t p_length = 0;
+       for (i=0; i < list->len; i++) {
+               param = buxton_array_get(list, i);
+               if (!param) {
+                       errno = EINVAL;
+                       goto fail;
+               }
+
+               switch (param->type) {
+               case STRING:
+                       p_length = param->store.d_string.length;
+                       break;
+               case INT32:
+                       p_length = sizeof(int32_t);
+                       break;
+               case UINT32:
+                       p_length = sizeof(uint32_t);
+                       break;
+               case INT64:
+                       p_length = sizeof(int64_t);
+                       break;
+               case UINT64:
+                       p_length = sizeof(uint64_t);
+                       break;
+               case FLOAT:
+                       p_length = sizeof(float);
+                       break;
+               case DOUBLE:
+                       p_length = sizeof(double);
+                       break;
+               case BOOLEAN:
+                       p_length = sizeof(bool);
+                       break;
+               default:
+                       errno = EINVAL;
+                       buxton_log("Invalid parameter type %lu\n", param->type);
+                       goto fail;
+               };
+
+               buxton_debug("offset: %lu\n", offset);
+               buxton_debug("value length: %lu\n", p_length);
+
+               /* Need to allocate enough room to hold this data */
+               size += sizeof(uint16_t) + sizeof(uint32_t) + p_length;
+
+               if (curSize < size) {
+                       if (!(data = greedy_realloc((void**)&data, &curSize, size))) {
+                               errno = ENOMEM;
+                               goto fail;
+                       }
+                       memzero(data+offset, size - offset);
+               }
+
+               /* Copy data type */
+               memcpy(data+offset, &(param->type), sizeof(uint16_t));
+               offset += sizeof(uint16_t);
+
+               /* Write out the length of value */
+               memcpy(data+offset, &p_length, sizeof(uint32_t));
+               offset += sizeof(uint32_t);
+
+               switch (param->type) {
+               case STRING:
+                       memcpy(data+offset, param->store.d_string.value, p_length);
+                       break;
+               case INT32:
+                       memcpy(data+offset, &(param->store.d_int32), sizeof(int32_t));
+                       break;
+               case UINT32:
+                       memcpy(data+offset, &(param->store.d_uint32), sizeof(uint32_t));
+                       break;
+               case INT64:
+                       memcpy(data+offset, &(param->store.d_int64), sizeof(int64_t));
+                       break;
+               case UINT64:
+                       memcpy(data+offset, &(param->store.d_uint64), sizeof(uint64_t));
+                       break;
+               case FLOAT:
+                       memcpy(data+offset, &(param->store.d_float), sizeof(float));
+                       break;
+               case DOUBLE:
+                       memcpy(data+offset, &(param->store.d_double), sizeof(double));
+                       break;
+               case BOOLEAN:
+                       memcpy(data+offset, &(param->store.d_boolean), sizeof(bool));
+                       break;
+               default:
+                       /* already tested this above, can't get here
+                        * normally */
+                       assert(0);
+               };
+               offset += p_length;
+               p_length = 0;
+       }
+
+       memcpy(data+BUXTON_LENGTH_OFFSET, &offset, sizeof(uint32_t));
+
+       ret = offset;
+       *dest = data;
+
+fail:
+       /* Clean up */
+       if (ret == 0) {
+               free(data);
+       }
+end:
+       buxton_debug("Serializing returned:%lu\n", ret);
+       return ret;
+}
+
+ssize_t buxton_deserialize_message(uint8_t *data,
+                                 BuxtonControlMessage *r_message,
+                                 size_t size, uint32_t *r_msgid,
+                                 BuxtonData **list)
+{
+       size_t offset = 0;
+       ssize_t ret = -1;
+       size_t min_length = BUXTON_MESSAGE_HEADER_LENGTH;
+       uint16_t control, message;
+       size_t n_params, c_param, c_length;
+       BuxtonDataType c_type = 0;
+       BuxtonData *k_list = NULL;
+       BuxtonData c_data;
+       uint32_t msgid;
+
+       assert(data);
+       assert(r_message);
+       assert(list);
+
+       buxton_debug("Deserializing message...\n");
+       buxton_debug("size=%lu\n", size);
+
+       if (size < min_length) {
+               errno = EINVAL;
+               goto end;
+       }
+
+       /* Copy the control code */
+       control = *(uint16_t*)data;
+       offset += sizeof(uint16_t);
+
+       /* Check this is a valid buxton message */
+       if (control != BUXTON_CONTROL_CODE) {
+               errno = EINVAL;
+               goto end;
+       }
+
+       /* Obtain the control message */
+       message = *(BuxtonControlMessage*)(data+offset);
+       offset += sizeof(uint16_t);
+
+       /* Ensure control message is in valid range */
+       if (message <= BUXTON_CONTROL_MIN || message >= BUXTON_CONTROL_MAX) {
+               errno = EINVAL;
+               goto end;
+       }
+
+       /* Skip size since our caller got this already */
+       offset += sizeof(uint32_t);
+
+       /* Obtain the message id */
+       msgid = *(uint32_t*)(data+offset);
+       offset += sizeof(uint32_t);
+
+       /* Obtain number of parameters */
+       n_params = *(uint32_t*)(data+offset);
+       offset += sizeof(uint32_t);
+       buxton_debug("total params: %d\n", n_params);
+
+       if (n_params > BUXTON_MESSAGE_MAX_PARAMS) {
+               errno = EINVAL;
+               goto end;
+       }
+
+       k_list = malloc0(sizeof(BuxtonData)*n_params);
+       if (n_params && !k_list) {
+               errno = ENOMEM;
+               goto end;
+       }
+
+       memzero(&c_data, sizeof(BuxtonData));
+
+       for (c_param = 0; c_param < n_params; c_param++) {
+               buxton_debug("param: %d\n", c_param + 1);
+               buxton_debug("offset=%lu\n", offset);
+               /* Don't read past the end of the buffer */
+               if (offset + sizeof(uint16_t) + sizeof(uint32_t) > size) {
+                       errno = EINVAL;
+                       goto end;
+               }
+
+               /* Now unpack type */
+               memcpy(&c_type, data+offset, sizeof(uint16_t));
+               offset += sizeof(uint16_t);
+
+               if (c_type >= BUXTON_TYPE_MAX || c_type <= BUXTON_TYPE_MIN) {
+                       errno = EINVAL;
+                       goto end;
+               }
+
+               /* Retrieve the length of the value */
+               c_length = *(uint32_t*)(data+offset);
+               if (c_length == 0 && c_type != STRING) {
+                       errno = EINVAL;
+                       goto end;
+               }
+               offset += sizeof(uint32_t);
+               buxton_debug("value length: %lu\n", c_length);
+
+               /* Don't try to read past the end of our buffer */
+               if (offset + c_length > size) {
+                       errno = EINVAL;
+                       goto end;
+               }
+
+               switch (c_type) {
+               case STRING:
+                       if (c_length) {
+                               c_data.store.d_string.value = malloc(c_length);
+                               if (!c_data.store.d_string.value) {
+                                       errno = ENOMEM;
+                                       goto end;
+                               }
+                               memcpy(c_data.store.d_string.value, data+offset, c_length);
+                               c_data.store.d_string.length = (uint32_t)c_length;
+                               if (c_data.store.d_string.value[c_length-1] != 0x00) {
+                                       errno = EINVAL;
+                                       buxton_debug("buxton_deserialize_message(): Garbage message\n");
+                                       free(c_data.store.d_string.value);
+                                       goto end;
+                               }
+                       } else {
+                               c_data.store.d_string.value = NULL;
+                               c_data.store.d_string.length = 0;
+                       }
+                       break;
+               case INT32:
+                       c_data.store.d_int32 = *(int32_t*)(data+offset);
+                       break;
+               case UINT32:
+                       c_data.store.d_uint32 = *(uint32_t*)(data+offset);
+                       break;
+               case INT64:
+                       c_data.store.d_int64 = *(int64_t*)(data+offset);
+                       break;
+               case UINT64:
+                       c_data.store.d_uint64 = *(uint64_t*)(data+offset);
+                       break;
+               case FLOAT:
+                       c_data.store.d_float = *(float*)(data+offset);
+                       break;
+               case DOUBLE:
+                       memcpy(&c_data.store.d_double, data + offset, sizeof(double));
+                       break;
+               case BOOLEAN:
+                       c_data.store.d_boolean = *(bool*)(data+offset);
+                       break;
+               default:
+                       errno = EINVAL;
+                       goto end;
+               }
+               c_data.type = c_type;
+               k_list[c_param] = c_data;
+               memzero(&c_data, sizeof(BuxtonData));
+               offset += c_length;
+       }
+       *r_message = message;
+       *r_msgid = msgid;
+       if (n_params == 0) {
+               *list = NULL;
+               free(k_list);
+               k_list = NULL;
+       } else {
+               *list = k_list;
+       }
+       ret = (ssize_t)n_params;
+end:
+       if (ret <= 0) {
+               free(k_list);
+       }
+
+       buxton_debug("Deserializing returned:%i\n", ret);
+       return ret;
+}
+
+size_t buxton_get_message_size(uint8_t *data, size_t size)
+{
+       size_t r_size;
+
+       assert(data);
+
+       if (size < BUXTON_MESSAGE_HEADER_LENGTH) {
+               return 0;
+       }
+
+       r_size = *(uint32_t*)(data + BUXTON_LENGTH_OFFSET);
+
+       if (r_size < BUXTON_MESSAGE_HEADER_LENGTH) {
+               return 0;
+       }
+
+       return r_size;
+}
+
+void include_serialize(void)
+{
+       ;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/serialize.h b/src/shared/serialize.h
new file mode 100644 (file)
index 0000000..3479fa8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file serialize.h Internal header
+ * This file is used internally by buxton to provide serialization
+ * functionality
+ */
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdint.h>
+
+#include "buxton.h"
+#include "buxtonarray.h"
+
+/**
+ * Magic for Buxton messages
+ */
+#define BUXTON_CONTROL_CODE 0x672
+
+/**
+ * Location of size in serialized message data
+ */
+#define BUXTON_LENGTH_OFFSET sizeof(uint32_t)
+
+/**
+ * Minimum size of serialized BuxtonData
+ * 2 is the minimum number of characters in a valid SMACK label
+ * 0 is the mimimum number of characters in a valid value (NULL STRING)
+ */
+#define BXT_MINIMUM_SIZE sizeof(BuxtonDataType) \
+       + (sizeof(uint32_t) * 2)                \
+       + 2
+
+/**
+ * Length of valid message header
+ */
+#define BUXTON_MESSAGE_HEADER_LENGTH sizeof(uint32_t)  \
+       + sizeof(uint32_t)
+/**
+ * Maximum length of valid control message
+ */
+#define BUXTON_MESSAGE_MAX_LENGTH 32768
+
+/**
+ * Maximum length of valid control message
+ */
+#define BUXTON_MESSAGE_MAX_PARAMS 16
+
+/**
+ * Serialize data internally for backend consumption
+ * @param source Data to be serialized
+ * @param label Label to be serialized
+ * @param target Pointer to store serialized data in
+ * @return a size_t value, indicating the size of serialized data
+ */
+size_t buxton_serialize(BuxtonData *source, BuxtonString *label,
+                       uint8_t **target)
+       __attribute__((warn_unused_result));
+
+/**
+ * Deserialize internal data for client consumption
+ * @param source Serialized data pointer
+ * @param target A pointer where the deserialize data will be stored
+ * @param label A pointer where the deserialize label will be stored
+ */
+void buxton_deserialize(uint8_t *source, BuxtonData *target,
+                       BuxtonString *label);
+
+/**
+ * Serialize an internal buxton message for wire communication
+ * @param dest Pointer to store serialized message in
+ * @param message The type of message to be serialized
+ * @param msgid The message ID to be serialized
+ * @param list An array of BuxtonData's to be serialized
+ * @return a size_t, 0 indicates failure otherwise size of dest
+ */
+size_t buxton_serialize_message(uint8_t **dest,
+                               BuxtonControlMessage message,
+                               uint32_t msgid,
+                               BuxtonArray *list)
+       __attribute__((warn_unused_result));
+
+/**
+ * Deserialize the given data into an array of BuxtonData structs
+ * @param data The source data to be deserialized
+ * @param r_message An empty pointer that will be set to the message type
+ * @param size The size of the data being deserialized
+ * @param r_msgid The message ID being deserialized
+ * @param list A pointer that will be filled out as an array of BuxtonData structs
+ * @return the length of the array, or -1 if deserialization failed
+ */
+ssize_t buxton_deserialize_message(uint8_t *data,
+                                 BuxtonControlMessage *r_message,
+                                 size_t size, uint32_t *r_msgid,
+                                 BuxtonData **list)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get size of a buxton message data stream
+ * @param data The source data stream
+ * @param size The size of the data stream (from read)
+ * @return a size_t length of the complete message or 0
+ */
+size_t buxton_get_message_size(uint8_t *data, size_t size)
+       __attribute__((warn_unused_result));
+
+void include_serialize(void);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/util.c b/src/shared/util.c
new file mode 100644 (file)
index 0000000..5cad2ef
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "configurator.h"
+#include "hashmap.h"
+#include "log.h"
+#include "util.h"
+
+size_t page_size(void)
+{
+       static __thread size_t pgsz = 0;
+       long r;
+
+       if (_likely_(pgsz > 0)) {
+               return pgsz;
+       }
+
+       r = sysconf(_SC_PAGESIZE);
+       assert(r > 0);
+
+       pgsz = (size_t) r;
+       return pgsz;
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need)
+{
+       size_t a;
+       void *q;
+
+       assert(p);
+       assert(allocated);
+
+       if (*allocated >= need) {
+               return *p;
+       }
+
+       a = MAX(64u, need * 2);
+       q = realloc(*p, a);
+       if (!q) {
+               return NULL;
+       }
+
+       *p = q;
+       *allocated = a;
+       return q;
+}
+
+char* get_layer_path(BuxtonLayer *layer)
+{
+       char *path = NULL;
+       int r;
+       char uid[15];
+
+       assert(layer);
+
+       switch (layer->type) {
+       case LAYER_SYSTEM:
+               r = asprintf(&path, "%s/%s.db", buxton_db_path(), layer->name.value);
+               if (r == -1) {
+                       return NULL;
+               }
+               break;
+       case LAYER_USER:
+               /* uid must already be set in layer before calling */
+               sprintf(uid, "%d", (int)layer->uid);
+               r = asprintf(&path, "%s/%s-%s.db", buxton_db_path(), layer->name.value, uid);
+               if (r == -1) {
+                       return NULL;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return path;
+}
+
+bool buxton_data_copy(BuxtonData* original, BuxtonData *copy)
+{
+       BuxtonDataStore store;
+
+       assert(original);
+       assert(copy);
+
+       switch (original->type) {
+       case STRING:
+               store.d_string.value = malloc(original->store.d_string.length);
+               if (!store.d_string.value) {
+                       goto fail;
+               }
+               memcpy(store.d_string.value, original->store.d_string.value, original->store.d_string.length);
+               store.d_string.length = original->store.d_string.length;
+               break;
+       case INT32:
+               store.d_int32 = original->store.d_int32;
+               break;
+       case UINT32:
+               store.d_uint32 = original->store.d_uint32;
+               break;
+       case INT64:
+               store.d_int64 = original->store.d_int64;
+               break;
+       case UINT64:
+               store.d_uint64 = original->store.d_uint64;
+               break;
+       case FLOAT:
+               store.d_float = original->store.d_float;
+               break;
+       case DOUBLE:
+               store.d_double = original->store.d_double;
+               break;
+       case BOOLEAN:
+               store.d_boolean = original->store.d_boolean;
+               break;
+       default:
+               goto fail;
+       }
+
+       copy->type = original->type;
+       copy->store = store;
+
+       return true;
+
+fail:
+       memset(copy, 0, sizeof(BuxtonData));
+       return false;
+}
+
+bool buxton_string_copy(BuxtonString *original, BuxtonString *copy)
+{
+       if (!original || !copy) {
+               return false;
+       }
+
+       copy->value = malloc0(original->length);
+       if (!copy->value) {
+               return false;
+       }
+
+       memcpy(copy->value, original->value, original->length);
+       copy->length = original->length;
+
+       return true;
+}
+
+bool buxton_key_copy(_BuxtonKey *original, _BuxtonKey *copy)
+{
+       if (!original || !copy) {
+               return false;
+       }
+
+       if (original->group.value) {
+               if (!buxton_string_copy(&original->group, &copy->group)) {
+                       goto fail;
+               }
+       }
+       if (original->name.value) {
+               if (!buxton_string_copy(&original->name, &copy->name)) {
+                       goto fail;
+               }
+       }
+       if (original->layer.value) {
+               if (!buxton_string_copy(&original->layer, &copy->layer)) {
+                       goto fail;
+               }
+       }
+       copy->type = original->type;
+
+       return true;
+
+fail:
+       if (original->group.value) {
+               free(copy->group.value);
+       }
+       if (original->name.value) {
+               free(copy->name.value);
+       }
+       if (original->layer.value) {
+               free(copy->layer.value);
+       }
+       copy->type = BUXTON_TYPE_MIN;
+
+       return false;
+}
+
+bool buxton_copy_key_group(_BuxtonKey *original, _BuxtonKey *group)
+{
+       if (!original || !group) {
+               return false;
+       }
+
+       if (original->group.value) {
+               if (!buxton_string_copy(&original->group, &group->group)) {
+                       goto fail;
+               }
+       }
+       group->name = (BuxtonString){ NULL, 0 };
+       if (original->layer.value) {
+               if (!buxton_string_copy(&original->layer, &group->layer)) {
+                       goto fail;
+               }
+       }
+       group->type = STRING;
+
+       return true;
+
+fail:
+       if (original->group.value) {
+               free(group->group.value);
+       }
+       if (original->layer.value) {
+               free(group->layer.value);
+       }
+       group->type = BUXTON_TYPE_MIN;
+
+       return false;
+}
+
+void data_free(BuxtonData *data)
+{
+       if (!data) {
+               return;
+       }
+
+       if (data->type == STRING && data->store.d_string.value) {
+               free(data->store.d_string.value);
+       }
+       free(data);
+}
+
+void string_free(BuxtonString *string)
+{
+       if (!string) {
+               return;
+       }
+
+       if (string->value) {
+               free(string->value);
+       }
+       free(string);
+}
+
+void key_free(_BuxtonKey *key)
+{
+       if (!key) {
+               return;
+       }
+
+       free(key->group.value);
+       free(key->name.value);
+       free(key->layer.value);
+       free(key);
+}
+
+const char* buxton_type_as_string(BuxtonDataType type)
+{
+       switch (type) {
+       case STRING:
+               return "string";
+       case INT32:
+               return "int32_t";
+       case UINT32:
+               return "uint32_t";
+       case INT64:
+               return "int64_t";
+       case UINT64:
+               return "uint64_t";
+       case FLOAT:
+               return "float";
+       case DOUBLE:
+               return "double";
+       case BOOLEAN:
+               return "boolean";
+       default:
+               return "[unknown]";
+       }
+}
+
+char *get_group(_BuxtonKey *key)
+{
+       if (key && key->group.value) {
+               return strdup(key->group.value);
+       }
+
+       return NULL;
+}
+
+char *get_name(_BuxtonKey *key)
+{
+       if (key && key->name.value) {
+               return strdup(key->name.value);
+       }
+
+       return NULL;
+}
+
+char *get_layer(_BuxtonKey *key)
+{
+       if (key && key->layer.value) {
+               return strdup(key->layer.value);
+       }
+
+       return NULL;
+}
+
+bool _write(int fd, uint8_t *buf, size_t nbytes)
+{
+       size_t nbytes_out = 0;
+
+       while (nbytes_out != nbytes) {
+               ssize_t b;
+               b = write(fd, buf + nbytes_out, nbytes - nbytes_out);
+
+               if (b == -1 && errno != EAGAIN) {
+                       buxton_debug("write error\n");
+                       return false;
+               }
+               nbytes_out += (size_t)b;
+       }
+
+       return true;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644 (file)
index 0000000..ebc53e6
--- /dev/null
@@ -0,0 +1,238 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+    #include "config.h"
+#endif
+
+#include <alloca.h>
+#include <inttypes.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/resource.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include "macro.h"
+#include "buxton.h"
+#include "buxtonkey.h"
+#include "backend.h"
+
+size_t page_size(void);
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+#define buxton_string_pack(s) ((BuxtonString){(s), (uint32_t)strlen(s) + 1})
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+bool streq_ptr(const char *a, const char *b) _pure_;
+
+static inline void freep(void *p)
+{
+       free(*(void**) p);
+}
+
+static inline void free_buxton_data(void *p)
+{
+       BuxtonData *s = (*(void**) p);
+       if (s && s->type == STRING) {
+               free(s->store.d_string.value);
+       }
+       free(s);
+}
+static inline void free_buxton_string(void *p)
+{
+       BuxtonString *s = (*(void**) p);
+       if (s) {
+               free(s->value);
+       }
+       free(s);
+}
+
+static inline void free_buxton_key(void *p)
+{
+       _BuxtonKey *k = (*(void**) p);
+       if (k) {
+               if (k->group.value) {
+                       free(k->group.value);
+               }
+               if (k->name.value) {
+                       free(k->name.value);
+               }
+               if (k->layer.value) {
+                       free(k->layer.value);
+               }
+       }
+       free(k);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_buxton_data_ _cleanup_(free_buxton_data)
+#define _cleanup_buxton_string_ _cleanup_(free_buxton_string)
+#define _cleanup_buxton_key_ _cleanup_(free_buxton_key)
+
+#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+
+#define malloc0(n) (calloc((n), 1))
+
+_malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
+       if (_unlikely_(b == 0 || a > ((size_t) -1) / b)) {
+               return NULL;
+       }
+
+       return malloc(a * b);
+}
+void* greedy_realloc(void **p, size_t *allocated, size_t need);
+
+/**
+ * Get the string representation of a BuxtonDataType
+ * @param type The BuxtonDataType to query
+ * @return A string representation of the BuxtonDataType
+ */
+const char* buxton_type_as_string(BuxtonDataType type)
+       __attribute__((warn_unused_result));
+
+/**
+ * Retrieve the filesystem path for the given layer
+ * @param layer The layer in question
+ * @return a string containing the filesystem path
+ */
+char* get_layer_path(BuxtonLayer *layer)
+       __attribute__((warn_unused_result));
+
+/**
+ * Perform a deep copy of one BuxtonData to another
+ * @param original The data being copied
+ * @param copy Pointer where original should be copied to
+ * @return A boolean indicating success or failure
+ */
+bool buxton_data_copy(BuxtonData *original, BuxtonData *copy);
+
+/**
+ * Perform a deep copy of one BuxtonString to another
+ * @param original The BuxtonString being copied
+ * @param copy Pointer where original should be copied to
+ */
+bool buxton_string_copy(BuxtonString *original, BuxtonString *copy)
+       __attribute__((warn_unused_result));
+
+/**
+ * Perform a deep copy of one _BuxtonKey to another
+ * @param original The _BuxtonKey being copied
+ * @param copy Pointer where original should be copied to
+ */
+bool buxton_key_copy(_BuxtonKey *original, _BuxtonKey *copy)
+       __attribute__((warn_unused_result));
+
+/**
+ * Perform a partial deep copy of a _BuxtonKey, omitting the 'name' member
+ * @param original The _BuxtonKey being copied
+ * @param group Pointer to the destination of the partial copy
+ */
+bool buxton_copy_key_group(_BuxtonKey *original, _BuxtonKey *group)
+       __attribute__((warn_unused_result));
+
+/**
+ * Perform a deep free of BuxtonData
+ * @param data The BuxtonData being free'd
+ */
+void data_free(BuxtonData *data);
+
+/**
+ * Perform a deep free of BuxtonString
+ * @param string The BuxtonString being free'd
+ */
+void string_free(BuxtonString *string);
+
+/**
+ * Perform a deep free of _BuxtonKey
+ * @param string The _BuxtonKey being free'd
+ */
+void key_free(_BuxtonKey *key);
+
+/**
+ * Get the group portion of a buxton key
+ * @param key Pointer to _BuxtonKey
+ * @return A pointer to a character string containing the key's group
+ */
+char *get_group(_BuxtonKey *key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the name portion of a buxton key
+ * @param key Pointer to _BuxtonKey
+ * @return A pointer to a character string containing the key's name
+ */
+char *get_name(_BuxtonKey *key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Get the layer portion of a buxton key
+ * @param key Pointer to _BuxtonKey
+ * @return A pointer to a character string containing the key's layer
+ */
+char *get_layer(_BuxtonKey *key)
+       __attribute__((warn_unused_result));
+
+/**
+ * Wrapper for nonblocking write, handles EAGAIN
+ * @param fd File descriptor to write to
+ * @param buf Buffer containing data to write
+ * @param len Length of buffer to write
+ * @return A boolean indicating the success of the operation
+ */
+bool _write(int fd, uint8_t *buf, size_t len)
+       __attribute__((warn_unused_result));
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_buxton.c b/test/check_buxton.c
new file mode 100644 (file)
index 0000000..883ae7e
--- /dev/null
@@ -0,0 +1,1229 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <check.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "backend.h"
+#include "buxton.h"
+#include "buxtonresponse.h"
+#include "check_utils.h"
+#include "configurator.h"
+#include "direct.h"
+#include "protocol.h"
+#include "serialize.h"
+#include "util.h"
+
+#ifdef NDEBUG
+#error "re-run configure with --enable-debug"
+#endif
+
+#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK"
+
+START_TEST(buxton_direct_open_check)
+{
+       BuxtonControl c;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_direct_init_db_check)
+{
+       BuxtonControl c;
+       BuxtonString slayer_name = buxton_string_pack("base");
+       BuxtonString ulayer_name = buxton_string_pack("user");
+       char db[PATH_MAX];
+       int r;
+       struct stat st;
+
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+
+       fail_if(!buxton_direct_init_db(&c, &ulayer_name),
+               "Failed to run init_db for user");
+
+       sprintf(db, "%s/user-%d.db", buxton_db_path(), getuid());
+       r = stat(db, &st);
+       fail_if(r != -1 && errno != ENOENT, "user db file created");
+
+       fail_if(!buxton_direct_init_db(&c, &slayer_name),
+               "Failed to run init_db");
+
+       sprintf(db, "%s/base.db", buxton_db_path());
+       r = stat(db, &st);
+       fail_if(r != 0, "Failed to create db file");
+
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_direct_create_group_check)
+{
+       BuxtonControl c;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       _BuxtonKey group;
+
+       group.layer = buxton_string_pack("base");
+       group.group = buxton_string_pack("tgroup");
+       group.name = (BuxtonString){ NULL, 0 };
+       group.type = STRING;
+       fail_if(!buxton_direct_create_group(&c, &group, NULL),
+               "Create group failed");
+}
+END_TEST
+
+START_TEST(buxton_direct_remove_group_check)
+{
+       BuxtonControl c;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       _BuxtonKey group;
+
+       group.layer = buxton_string_pack("base");
+       group.group = buxton_string_pack("tgroup");
+       group.name = (BuxtonString){ NULL, 0 };
+       group.type = STRING;
+       fail_if(!buxton_direct_remove_group(&c, &group, NULL),
+               "Failed to remove group");
+}
+END_TEST
+
+START_TEST(buxton_direct_set_value_check)
+{
+       BuxtonControl c;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       _BuxtonKey group;
+       BuxtonString glabel;
+       _BuxtonKey key;
+       BuxtonData data;
+
+       group.layer = buxton_string_pack("test-gdbm");
+       group.group = buxton_string_pack("bxt_test_group");
+       group.name = (BuxtonString){ NULL, 0 };
+       group.type = STRING;
+       glabel = buxton_string_pack("*");
+
+       key.layer = group.layer;
+       key.group = group.group;
+       key.name = buxton_string_pack("bxt_test_key");
+       key.type = STRING;
+
+       c.client.uid = getuid();
+       fail_if(buxton_direct_create_group(&c, &group, NULL) == false,
+               "Creating group failed.");
+       fail_if(buxton_direct_set_label(&c, &group, &glabel) == false,
+               "Setting group label failed.");
+       data.type = STRING;
+       data.store.d_string = buxton_string_pack("bxt_test_value");
+       fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false,
+               "Setting value in buxton directly failed.");
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_direct_get_value_for_layer_check)
+{
+       BuxtonControl c;
+       BuxtonData result;
+       BuxtonString dlabel;
+       _BuxtonKey key;
+
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("bxt_test_group");
+       key.name = buxton_string_pack("bxt_test_key");
+       key.type = STRING;
+
+       c.client.uid = getuid();
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Retrieving value from buxton gdbm backend failed.");
+       fail_if(result.type != STRING,
+               "Buxton gdbm backend returned incorrect result type.");
+       //FIXME get label test figured out
+       fail_if(strcmp(result.store.d_string.value, "bxt_test_value") != 0,
+               "Buxton gdbm returned a different value to that set.");
+       if (result.store.d_string.value)
+               free(result.store.d_string.value);
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_direct_get_value_check)
+{
+       BuxtonControl c;
+       BuxtonData data, result;
+       BuxtonString dlabel;
+       _BuxtonKey key;
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("bxt_test_group");
+       key.name = buxton_string_pack("bxt_test_key");
+       key.type = STRING;
+
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+
+       c.client.uid = getuid();
+       data.type = STRING;
+       data.store.d_string = buxton_string_pack("bxt_test_value2");
+       fail_if(data.store.d_string.value == NULL,
+               "Failed to allocate test string.");
+       fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false,
+               "Failed to set second value.");
+       fail_if(buxton_direct_get_value(&c, &key, &result, &dlabel, NULL) == -1,
+               "Retrieving value from buxton gdbm backend failed.");
+       fail_if(result.type != STRING,
+               "Buxton gdbm backend returned incorrect result type.");
+       //FIXME figure out label check
+       fail_if(strcmp(result.store.d_string.value, "bxt_test_value2") != 0,
+               "Buxton gdbm returned a different value to that set.");
+       if (result.store.d_string.value)
+               free(result.store.d_string.value);
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_memory_backend_check)
+{
+       BuxtonControl c;
+       BuxtonData data, result;
+       BuxtonString dlabel, glabel;
+       _BuxtonKey group;
+       _BuxtonKey key;
+
+       group.layer = buxton_string_pack("temp");
+       group.group = buxton_string_pack("bxt_mem_test_group");
+       group.name = (BuxtonString){ NULL, 0 };
+       group.type = STRING;
+       glabel = buxton_string_pack("*");
+
+       key.layer = group.layer;
+       key.group = group.group;
+       key.name = buxton_string_pack("bxt_mem_test_key");
+       key.type = STRING;
+
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+
+       c.client.uid = getuid();
+       fail_if(buxton_direct_create_group(&c, &group, NULL) == false,
+               "Creating group failed.");
+       fail_if(buxton_direct_set_label(&c, &group, &glabel) == false,
+               "Setting group label failed.");
+       data.type = STRING;
+       data.store.d_string = buxton_string_pack("bxt_test_value");
+       fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false,
+               "Setting value in buxton memory backend directly failed.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Retrieving value from buxton memory backend directly failed.");
+       // FIXME: BUXTON_GROUP_VALUE is the dummy group data value, but the memory
+       // backend doesn't understand groups, so this is the current workaround.
+       fail_if(!streq(result.store.d_string.value, "bxt_test_value"),
+               "Buxton memory returned a different value to that set.");
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_key_check)
+{
+       char *group = "group";
+       char *name = "name";
+       char *layer = "layer";
+       BuxtonKey key;
+       char *g;
+       char *n;
+       char *l;
+       BuxtonDataType type = STRING;
+
+       key = buxton_key_create(group, name, layer, type);
+       fail_if(!key, "Failed to create buxton key");
+       g = buxton_key_get_group(key);
+       n = buxton_key_get_name(key);
+       l = buxton_key_get_layer(key);
+       fail_if(!g, "Failed to get group from key");
+       fail_if(!n, "Failed to get name from key");
+       fail_if(!l, "Failed to get layer from key");
+       fail_if(buxton_key_get_type(key) != type,
+               "Failed to get correct type from key");
+       fail_if(!streq(g, group), "Got different group back from key");
+       fail_if(!streq(n, name), "Got different name back from key");
+       fail_if(!streq(l, layer), "Got different layer back from key");
+       free(g);
+       free(n);
+       free(l);
+       buxton_key_free(key);
+
+       fail_if(buxton_key_create(NULL, name, layer, type), "Got key back with invalid group");
+       fail_if(buxton_key_create(group, name, layer, BUXTON_TYPE_MIN),
+               "Got key back with invalid type 1");
+       fail_if(buxton_key_create(group, name, layer, BUXTON_TYPE_MAX),
+               "Got key back with invalid type 2");
+
+       key = buxton_key_create(group, NULL, layer, type);
+       fail_if(!key, "Failed to create buxton key with empty name");
+       fail_if(buxton_key_get_name(key), "Got name back with no name key");
+       buxton_key_free(key);
+
+       key = buxton_key_create(group, name, NULL, type);
+       fail_if(!key, "Failed to create buxton key with empty layer");
+       fail_if(buxton_key_get_layer(key), "Got layer back with no layer key");
+       buxton_key_free(key);
+}
+END_TEST
+
+START_TEST(buxton_set_label_check)
+{
+       BuxtonControl c;
+       BuxtonString label = buxton_string_pack("*");
+       _BuxtonKey key;
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("bxt_test");
+       key.name.value = NULL;
+       key.type = STRING;
+       char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+       bool skip_check = (root_check && streq(root_check, "0"));
+
+       c.client.uid = 0;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       fail_if(buxton_direct_create_group(&c, &key, NULL) == false,
+               "Creating group failed.");
+       fail_if(buxton_direct_set_label(&c, &key, &label) == false,
+               "Failed to set label as root user.");
+
+       c.client.uid = 1000;
+
+       if (skip_check)
+               fail_if(!buxton_direct_set_label(&c, &key, &label),
+                       "Unable to set label with root check disabled");
+       else
+               fail_if(buxton_direct_set_label(&c, &key, &label),
+                       "Able to set label as non-root user.");
+
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_group_label_check)
+{
+       BuxtonControl c;
+       BuxtonData result;
+       BuxtonString dlabel;
+       BuxtonString label = buxton_string_pack("*");
+       _BuxtonKey key;
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("test-group");
+       key.name.value = NULL;
+       key.type = STRING;
+
+
+       c.client.uid = 0;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       fail_if(buxton_direct_create_group(&c, &key, NULL) == false,
+               "Creating group failed.");
+       fail_if(buxton_direct_set_label(&c, &key, &label) == false,
+               "Failed to set group label.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Retrieving group label failed.");
+       fail_if(!streq("*", dlabel.value),
+               "Retrieved group label is incorrect.");
+
+       free(dlabel.value);
+       buxton_direct_close(&c);
+}
+END_TEST
+
+START_TEST(buxton_name_label_check)
+{
+       BuxtonControl c;
+       BuxtonData data, result;
+       BuxtonString label, dlabel;
+       _BuxtonKey key;
+
+       /* create the group first, and validate the label */
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("group-foo");
+       key.name.value = NULL;
+       key.type = STRING;
+       label = buxton_string_pack("*");
+
+       c.client.uid = 0;
+       fail_if(buxton_direct_open(&c) == false,
+               "Direct open failed without daemon.");
+       fail_if(buxton_direct_create_group(&c, &key, NULL) == false,
+               "Creating group failed.");
+       fail_if(buxton_direct_set_label(&c, &key, &label) == false,
+               "Failed to set group label.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Retrieving group label failed.");
+       fail_if(!streq("*", dlabel.value),
+               "Retrieved group label is incorrect.");
+       free(dlabel.value);
+       free(result.store.d_string.value);
+
+       /* then create the name (key), and validate the label */
+       key.name = buxton_string_pack("name-foo");
+       data.type = STRING;
+       data.store.d_string = buxton_string_pack("value1-foo");
+       fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false,
+               "Failed to set key name-foo.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Failed to get value for name-foo 1");
+       fail_if(!streq("value1-foo", result.store.d_string.value),
+               "Retrieved key value is incorrect 1");
+       fail_if(!streq(dlabel.value, "_"), "Failed to set default label");
+       free(dlabel.value);
+       free(result.store.d_string.value);
+       fail_if(buxton_direct_set_label(&c, &key, &label) == false,
+               "Failed to set name label.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Failed to get value for name-foo 2");
+       fail_if(!streq("value1-foo", result.store.d_string.value),
+               "Retrieved key value is incorrect 2");
+       fail_if(!streq("*", dlabel.value),
+               "Retrieved key label is incorrect.");
+       free(dlabel.value);
+       free(result.store.d_string.value);
+
+       /* modify the same key, with a new value, and validate the label */
+       data.store.d_string = buxton_string_pack("value2-foo");
+       fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false,
+               "Failed to modify key name-foo.");
+       fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL),
+               "Failed to get new value for name-foo.");
+       fail_if(!streq("value2-foo", result.store.d_string.value),
+               "New key value is incorrect.");
+       fail_if(!streq("*", dlabel.value),
+               "Key label has been modified.");
+
+       /* modify the key label directly once it has been created */
+       fail_if(buxton_direct_set_label(&c, &key, &label) == false,
+               "Failed to modify label on key.");
+
+       free(dlabel.value);
+       free(result.store.d_string.value);
+
+       buxton_direct_close(&c);
+}
+END_TEST
+
+static int run_callback_test_value = 0;
+static void run_callback_cb_test(BuxtonResponse response, void *data)
+{
+       _BuxtonResponse *r = (_BuxtonResponse *)response;
+       BuxtonData *d;
+       bool *b;
+
+       fail_if(r->type != BUXTON_CONTROL_SET, "Unexpected response type");
+
+       fail_if(!streq(r->key->group.value, "group"),
+               "Failed to set key's group");
+
+       switch (run_callback_test_value) {
+       case 0:
+               /* first pass through */
+               run_callback_test_value = 1;
+               break;
+       case 1:
+               /* second pass through */
+               fail_if(r->data->len != 1, "Failed setup array size");
+               d = buxton_array_get(r->data, 0);
+               fail_if(!d, "Failed to set array element");
+               fail_if(d->type != INT32, "Failed to setup array element value");
+               run_callback_test_value = 2;
+               break;
+       case 2:
+               /* third pass through */
+               b = (bool *)data;
+               *b = true;
+               break;
+       default:
+               fail("Unexpected test value");
+               break;
+       }
+}
+START_TEST(run_callback_check)
+{
+       bool data = false;
+       BuxtonData list[] = {
+               {INT32, {.d_int32 = 1}}
+       };
+       _BuxtonKey key;
+       key.group = buxton_string_pack("group");
+
+       run_callback(NULL, (void *)&data, 1, list, BUXTON_CONTROL_SET, &key);
+       run_callback(run_callback_cb_test, NULL, 0, NULL, BUXTON_CONTROL_SET,
+                    &key);
+       fail_if(run_callback_test_value != 1,
+               "Failed to update callback test value 1");
+       run_callback(run_callback_cb_test, NULL, 1, list, BUXTON_CONTROL_SET,
+                    &key);
+       fail_if(run_callback_test_value != 2,
+               "Failed to update callback test value 2");
+       run_callback(run_callback_cb_test, (void *)&data, 0, NULL,
+                    BUXTON_CONTROL_SET, &key);
+       fail_if(!data, "Failed to update callback test value 3");
+}
+END_TEST
+
+START_TEST(send_message_check)
+{
+       _BuxtonClient client;
+       BuxtonArray *out_list = NULL;
+       BuxtonData *list = NULL;
+       int server;
+       uint8_t *dest = NULL;
+       uint8_t *source = NULL;
+       size_t size;
+       BuxtonData data;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to setup callbacks");
+
+       out_list = buxton_array_new();
+       data.type = INT32;
+       data.store.d_int32 = 0;
+       fail_if(!buxton_array_add(out_list, &data),
+               "Failed to add get response to array");
+       size = buxton_serialize_message(&source, BUXTON_CONTROL_STATUS,
+                                       0, out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       fail_if(!send_message(&client, source, size, NULL, NULL, 0,
+                             BUXTON_CONTROL_STATUS, NULL),
+               "Failed to write message 1");
+
+       cleanup_callbacks();
+       buxton_array_free(&out_list, NULL);
+       free(dest);
+       free(list);
+       close(server);
+       close(client.fd);
+}
+END_TEST
+
+static void handle_response_cb_test(_BuxtonResponse *response, void *data)
+{
+       bool *val = (bool *)data;
+       fail_if(!(*val), "Got unexpected response data");
+       *val = false;
+}
+START_TEST(handle_callback_response_check)
+{
+       _BuxtonClient client;
+       BuxtonArray *out_list = NULL;
+       uint8_t *dest = NULL;
+       int server;
+       size_t size;
+       bool test_data;
+       BuxtonData data;
+       uint32_t msgid;
+       BuxtonData good[] = {
+               {INT32, {.d_int32 = 0}}
+       };
+       BuxtonData good_unnotify[] = {
+               {INT32,  {.d_int32 = 0   }},
+               {STRING, {.d_string = {0}}},
+               {UINT32, {.d_uint32 = 4  }}
+       };
+       BuxtonData bad1[] = {
+               {INT64, {.d_int64 = 0}}
+       };
+       BuxtonData bad2[] = {
+               {INT32, {.d_int32 = 1}}
+       };
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       /* done just to create a callback to be used */
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze response callbacks");
+       out_list = buxton_array_new();
+       data.type = INT32;
+       data.store.d_int32 = 0;
+       msgid = 1;
+       fail_if(!buxton_array_add(out_list, &data),
+               "Failed to add data to array");
+       size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS,
+                                       msgid, out_list);
+       buxton_array_free(&out_list, NULL);
+       fail_if(size == 0, "Failed to serialize message");
+
+       test_data = true;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_SET, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1);
+       fail_if(test_data, "Failed to set cb data non notify type");
+
+       test_data = true;
+       msgid = 2;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1);
+       fail_if(test_data, "Failed to set notify bad1 data");
+
+       test_data = true;
+       msgid = 3;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad2, 1);
+       fail_if(test_data, "Failed to set notify bad2 data");
+
+       test_data = true;
+       msgid = 4;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good, 1);
+       fail_if(!test_data, "Set notify good data");
+
+       /* ensure we run callback on duplicate msgid */
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL),
+               "Failed to send message %d-2", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good, 1);
+       fail_if(test_data, "Failed to set notify duplicate msgid");
+
+       test_data = true;
+       handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1);
+       fail_if(test_data, "Failed to set changed data");
+
+       /* ensure we don't remove callback on changed */
+       test_data = true;
+       handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1);
+       fail_if(test_data, "Failed to set changed data");
+
+       test_data = true;
+       msgid = 6;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1);
+       fail_if(test_data, "Failed to set unnotify bad1 data");
+
+       test_data = true;
+       msgid = 7;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad2, 1);
+       fail_if(test_data, "Failed to set unnotify bad2 data");
+
+       test_data = true;
+       msgid = 8;
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL),
+               "Failed to send message %d", msgid);
+       handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good_unnotify, 1);
+       fail_if(!test_data, "Set unnotify good data");
+
+       test_data = true;
+       msgid = 4;
+       handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1);
+       fail_if(!test_data, "Didn't remove changed callback");
+
+       cleanup_callbacks();
+       free(dest);
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_handle_response_check)
+{
+       _BuxtonClient client;
+       BuxtonArray *out_list = NULL;
+       int server;
+       uint8_t *dest = NULL;
+       size_t size;
+       BuxtonData data;
+       bool test_data = true;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       /* done just to create a callback to be used */
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze get response callbacks");
+       out_list = buxton_array_new();
+       data.type = INT32;
+       data.store.d_int32 = 0;
+       fail_if(!buxton_array_add(out_list, &data),
+               "Failed to add get response to array");
+       size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS,
+                                       0, out_list);
+       buxton_array_free(&out_list, NULL);
+       fail_if(size == 0, "Failed to serialize message");
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, 0, BUXTON_CONTROL_STATUS, NULL),
+               "Failed to send message");
+
+       /* server */
+       fail_if(!_write(server, dest, size),
+               "Failed to send get response");
+
+       /* client */
+       fail_if(buxton_wire_handle_response(&client) != 1,
+               "Failed to handle response correctly");
+       fail_if(test_data, "Failed to update data");
+
+       cleanup_callbacks();
+       free(dest);
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_get_response_check)
+{
+       _BuxtonClient client;
+       BuxtonArray *out_list = NULL;
+       int server;
+       uint8_t *dest = NULL;
+       size_t size;
+       BuxtonData data;
+       bool test_data = true;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       /* done just to create a callback to be used */
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze callbacks");
+       out_list = buxton_array_new();
+       data.type = INT32;
+       data.store.d_int32 = 0;
+       fail_if(!buxton_array_add(out_list, &data),
+               "Failed to add data to array");
+       size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS,
+                                       0, out_list);
+       buxton_array_free(&out_list, NULL);
+       fail_if(size == 0, "Failed to serialize message");
+       fail_if(!send_message(&client, dest, size, handle_response_cb_test,
+                             &test_data, 0, BUXTON_CONTROL_STATUS, NULL),
+               "Failed to send message");
+
+       /* server */
+       fail_if(!_write(server, dest, size),
+               "Failed to send get response");
+
+       /* client */
+       fail_if(!buxton_wire_get_response(&client),
+               "Failed to handle response correctly");
+       fail_if(test_data, "Failed to update data");
+
+       cleanup_callbacks();
+       free(dest);
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_set_value_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze callbacks");
+
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name");
+       key.type = STRING;
+
+       fail_if(buxton_wire_set_value(&client, &key, "value", NULL,
+                                     NULL) != true,
+               "Failed to properly set value");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 4, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_SET,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(list[2].type != STRING, "Failed to set correct name type");
+       fail_if(list[3].type != STRING, "Failed to set correct value type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+       fail_if(!streq(list[2].store.d_string.value, "name"),
+               "Failed to set correct name");
+       fail_if(!streq(list[3].store.d_string.value, "value"),
+               "Failed to set correct value");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list[2].store.d_string.value);
+       free(list[3].store.d_string.value);
+       free(list);
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_set_label_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonString value;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       client.uid = 0;
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialize callbacks");
+
+       /* first, set a label on a group */
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name.value = NULL;
+       value = buxton_string_pack("*");
+       fail_if(buxton_wire_set_label(&client, &key, &value, NULL,
+                                     NULL) != true,
+               "Failed to properly set label");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 3, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_SET_LABEL,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(list[2].type != STRING, "Failed to set correct label type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+       fail_if(!streq(list[2].store.d_string.value, "*"),
+               "Failed to set correct label");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list[2].store.d_string.value);
+       free(list);
+
+       /* ... then, set a label on a key */
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name");
+       value = buxton_string_pack("*");
+       fail_if(buxton_wire_set_label(&client, &key, &value, NULL,
+                                     NULL) != true,
+               "Failed to properly set label");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 4, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_SET_LABEL,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(list[2].type != STRING, "Failed to set correct name type");
+       fail_if(list[3].type != STRING, "Failed to set correct label type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+       fail_if(!streq(list[2].store.d_string.value, "name"),
+               "Failed to set correct name");
+       fail_if(!streq(list[3].store.d_string.value, "*"),
+               "Failed to set correct label");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list[2].store.d_string.value);
+       free(list[3].store.d_string.value);
+       free(list);
+
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_get_value_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze callbacks");
+
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name");
+       key.type = STRING;
+       fail_if(buxton_wire_get_value(&client, &key, NULL,
+                                     NULL) != true,
+               "Failed to properly get value 1");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed 1");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 4, "Failed to get valid message from buffer 1");
+       fail_if(msg != BUXTON_CONTROL_GET,
+               "Failed to get correct control type 1");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type 1");
+       fail_if(list[1].type != STRING, "Failed to set correct group type 1");
+       fail_if(list[2].type != STRING, "Failed to set correct name type 1");
+       fail_if(list[3].type != UINT32, "Failed to set correct type type 1");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer 1");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group 1");
+       fail_if(!streq(list[2].store.d_string.value, "name"),
+               "Failed to set correct name 1");
+       fail_if(list[3].store.d_uint32 != STRING,
+               "Failed to set correct type 1");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list[2].store.d_string.value);
+       free(list);
+
+       key.layer.value = NULL;
+       fail_if(buxton_wire_get_value(&client, &key, NULL,
+                                     NULL) != true,
+               "Failed to properly get value 2");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed 2");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 3, "Failed to get valid message from buffer 2");
+       fail_if(msg != BUXTON_CONTROL_GET,
+               "Failed to get correct control type 2");
+       fail_if(list[0].type != STRING, "Failed to set correct group type 2");
+       fail_if(list[1].type != STRING, "Failed to set correct name type 2");
+       fail_if(list[2].type != UINT32, "Failed to set correct type type 2");
+       fail_if(!streq(list[0].store.d_string.value, "group"),
+               "Failed to set correct group 2");
+       fail_if(!streq(list[1].store.d_string.value, "name"),
+               "Failed to set correct name 2");
+       fail_if(list[2].store.d_uint32 != STRING,
+               "Failed to set correct type 2");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list);
+
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_unset_value_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialeze callbacks");
+
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name");
+       key.type = STRING;
+       fail_if(!buxton_wire_unset_value(&client, &key, NULL,
+                                        NULL),
+               "Failed to properly unset value");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 4, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_UNSET,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(list[2].type != STRING, "Failed to set correct name type");
+       fail_if(list[3].type != UINT32, "Failed to set correct type type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+       fail_if(!streq(list[2].store.d_string.value, "name"),
+               "Failed to set correct group");
+       fail_if(list[3].store.d_uint32 != STRING,
+               "Failed to set correct type");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list[2].store.d_string.value);
+       free(list);
+
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_create_group_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       client.uid = 0;
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialize callbacks");
+
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name.value = NULL;
+       fail_if(buxton_wire_create_group(&client, &key, NULL,
+                                        NULL) != true,
+               "Failed to send message");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 2, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_CREATE_GROUP,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list);
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(buxton_wire_remove_group_check)
+{
+       _BuxtonClient client;
+       int server;
+       ssize_t size;
+       BuxtonData *list = NULL;
+       uint8_t buf[4096];
+       ssize_t r;
+       _BuxtonKey key;
+       BuxtonControlMessage msg;
+       uint32_t msgid;
+
+       client.uid = 0;
+       setup_socket_pair(&(client.fd), &server);
+       fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       fail_if(!setup_callbacks(),
+               "Failed to initialize callbacks");
+
+       key.layer = buxton_string_pack("layer");
+       key.group = buxton_string_pack("group");
+       key.name.value = NULL;
+       fail_if(buxton_wire_remove_group(&client, &key, NULL,
+                                        NULL) != true,
+               "Failed to send message");
+
+       r = read(server, buf, 4096);
+       fail_if(r < 0, "Read from client failed");
+       size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list);
+       fail_if(size != 2, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_REMOVE_GROUP,
+               "Failed to get correct control type");
+       fail_if(list[0].type != STRING, "Failed to set correct layer type");
+       fail_if(list[1].type != STRING, "Failed to set correct group type");
+       fail_if(!streq(list[0].store.d_string.value, "layer"),
+               "Failed to set correct layer");
+       fail_if(!streq(list[1].store.d_string.value, "group"),
+               "Failed to set correct group");
+
+       free(list[0].store.d_string.value);
+       free(list[1].store.d_string.value);
+       free(list);
+       cleanup_callbacks();
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+static Suite *
+buxton_suite(void)
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("buxton");
+
+       tc = tcase_create("buxton_client_lib_functions");
+       tcase_add_test(tc, buxton_direct_init_db_check);
+       tcase_add_test(tc, buxton_direct_open_check);
+       tcase_add_test(tc, buxton_direct_create_group_check);
+       tcase_add_test(tc, buxton_direct_remove_group_check);
+       tcase_add_test(tc, buxton_direct_set_value_check);
+       tcase_add_test(tc, buxton_direct_get_value_for_layer_check);
+       tcase_add_test(tc, buxton_direct_get_value_check);
+       tcase_add_test(tc, buxton_memory_backend_check);
+       tcase_add_test(tc, buxton_key_check);
+       tcase_add_test(tc, buxton_set_label_check);
+       tcase_add_test(tc, buxton_group_label_check);
+       tcase_add_test(tc, buxton_name_label_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("buxton_protocol_functions");
+       tcase_add_test(tc, run_callback_check);
+       tcase_add_test(tc, handle_callback_response_check);
+       tcase_add_test(tc, send_message_check);
+       tcase_add_test(tc, buxton_wire_handle_response_check);
+       tcase_add_test(tc, buxton_wire_get_response_check);
+       tcase_add_test(tc, buxton_wire_set_value_check);
+       tcase_add_test(tc, buxton_wire_set_label_check);
+       tcase_add_test(tc, buxton_wire_get_value_check);
+       tcase_add_test(tc, buxton_wire_unset_value_check);
+       tcase_add_test(tc, buxton_wire_create_group_check);
+       tcase_add_test(tc, buxton_wire_remove_group_check);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
+
+int main(void)
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf");
+       putenv("BUXTON_ROOT_CHECK=0");
+       s = buxton_suite();
+       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;
+}
+
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_configurator.c b/test/check_configurator.c
new file mode 100644 (file)
index 0000000..d18881b
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <check.h>
+#include <iniparser.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "configurator.h"
+
+#ifdef NDEBUG
+       #error "re-run configure with --enable-debug"
+#endif
+
+static void fail_strne(const char *value, char *correct_value, bool casecmp)
+{
+       char buf[PATH_MAX];
+       int ret;
+
+       snprintf(buf, sizeof(buf), "%s was not %s", value, correct_value);
+       if (casecmp)
+               ret = strcasecmp(value, correct_value);
+       else
+               ret = strcmp(value, correct_value);
+       fail_unless(ret == 0, buf);
+}
+
+static void fail_ne(int a, int b)
+{
+       fail_unless(a == b, "%d is not %d", a, b);
+}
+
+static void default_test(const char *value, char *correct_value, char *symbolname)
+{
+       char buf[PATH_MAX];
+
+       snprintf(buf, sizeof(buf), "%s returned null!", symbolname);
+       fail_if(value == NULL, buf);
+
+       snprintf(buf, sizeof(buf), "%s was not %s", value, correct_value);
+       fail_strne(value, correct_value, true);
+}
+
+START_TEST(configurator_default_module_dir)
+{
+       default_test(buxton_module_dir(), (char*)_MODULE_DIRECTORY, "buxton_module_dir()");
+}
+END_TEST
+
+START_TEST(configurator_default_conf_file)
+{
+       default_test(buxton_conf_file(), (char*)_DEFAULT_CONFIGURATION_FILE, "buxton_conf_file()");
+}
+END_TEST
+
+START_TEST(configurator_default_db_path)
+{
+       default_test(buxton_db_path(), (char*)_DB_PATH, "buxton_db_path()");
+}
+END_TEST
+
+START_TEST(configurator_default_smack_load_file)
+{
+       default_test(buxton_smack_load_file(), (char*)_SMACK_LOAD_FILE, "buxton_smack_load_file()");
+}
+END_TEST
+
+START_TEST(configurator_default_buxton_socket)
+{
+       default_test(buxton_socket(), (char*)_BUXTON_SOCKET, "buxton_socket()");
+}
+END_TEST
+
+
+START_TEST(configurator_env_conf_file)
+{
+       putenv("BUXTON_CONF_FILE=/nonexistant/buxton.conf");
+       default_test(buxton_conf_file(), "/nonexistant/buxton.conf", "buxton_conf_file()");
+}
+END_TEST
+
+START_TEST(configurator_env_module_dir)
+{
+       putenv("BUXTON_MODULE_DIR=/nonexistant/buxton/plugins");
+       default_test(buxton_module_dir(), "/nonexistant/buxton/plugins", "buxton_module_dir()");
+}
+END_TEST
+
+START_TEST(configurator_env_db_path)
+{
+       putenv("BUXTON_DB_PATH=/nonexistant/buxton.db");
+       default_test(buxton_db_path(), "/nonexistant/buxton.db", "buxton_db_path()");
+}
+END_TEST
+
+START_TEST(configurator_env_smack_load_file)
+{
+       putenv("BUXTON_SMACK_LOAD_FILE=/nonexistant/smack_load_file");
+       default_test(buxton_smack_load_file(), "/nonexistant/smack_load_file", "buxton_smack_load_file()");
+}
+END_TEST
+
+START_TEST(configurator_env_buxton_socket)
+{
+       putenv("BUXTON_BUXTON_SOCKET=/nonexistant/buxton_socket");
+       default_test(buxton_socket(), "/nonexistant/buxton_socket", "buxton_socket()");
+}
+END_TEST
+
+
+START_TEST(configurator_cmd_conf_file)
+{
+       char *correct = "herpyderpy";
+
+       buxton_add_cmd_line(CONFIG_CONF_FILE, correct);
+       putenv("BUXTON_CONF_FILE=/nonexistant/buxton.conf");
+       default_test(buxton_conf_file(), correct, "buxton_conf_file()");
+}
+END_TEST
+
+START_TEST(configurator_cmd_module_dir)
+{
+       char *correct = "herpyderpy";
+
+       buxton_add_cmd_line(CONFIG_MODULE_DIR, correct);
+       putenv("BUXTON_MODULE_DIR=/nonexistant/buxton/plugins");
+       default_test(buxton_module_dir(), correct, "buxton_module_dir()");
+}
+END_TEST
+
+START_TEST(configurator_cmd_db_path)
+{
+       char *correct = "herpyderpy";
+
+       buxton_add_cmd_line(CONFIG_DB_PATH, correct);
+       putenv("BUXTON_DB_PATH=/nonexistant/buxton.db");
+       default_test(buxton_db_path(), correct, "buxton_db_path()");
+}
+END_TEST
+
+START_TEST(configurator_cmd_smack_load_file)
+{
+       char *correct = "herpyderpy";
+
+       buxton_add_cmd_line(CONFIG_SMACK_LOAD_FILE, correct);
+       putenv("BUXTON_SMACK_LOAD_FILE=/nonexistant/smack_load_file");
+       default_test(buxton_smack_load_file(), correct, "buxton_smack_load_file()");
+}
+END_TEST
+
+START_TEST(configurator_cmd_buxton_socket)
+{
+       char *correct = "herpyderpy";
+
+       buxton_add_cmd_line(CONFIG_BUXTON_SOCKET, correct);
+       putenv("BUXTON_BUXTON_SOCKET=/nonexistant/buxton_socket");
+       default_test(buxton_socket(), correct, "buxton_socket()");
+}
+END_TEST
+
+
+START_TEST(configurator_conf_db_path)
+{
+       char *correct = "/you/are/so/suck";
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf");
+       default_test(buxton_db_path(), correct, "buxton_db_path()");
+}
+END_TEST
+
+START_TEST(configurator_conf_smack_load_file)
+{
+       char *correct = "/smack/smack/smack";
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf");
+       default_test(buxton_smack_load_file(), correct, "smack_load_file()");
+}
+END_TEST
+
+START_TEST(configurator_conf_buxton_socket)
+{
+       char *correct = "/hurp/durp/durp";
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf");
+       default_test(buxton_socket(), correct, "buxton_socket()");
+}
+END_TEST
+
+START_TEST(configurator_conf_module_dir)
+{
+       char *correct = "/shut/your/mouth";
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf");
+       default_test(buxton_module_dir(), correct, "buxton_module_dir()");
+}
+END_TEST
+
+START_TEST(configurator_get_layers)
+{
+       ConfigLayer *layers = NULL;
+       int numlayers;
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf");
+       numlayers = buxton_key_get_layers(&layers);
+       fail_if(layers == NULL, "buxton_key_get_layers returned NULL");
+       fail_if(numlayers != 7, "num layers is %d instead of %d", numlayers, 7);
+
+       fail_strne(layers[0].name, "base", false);
+       fail_strne(layers[0].type, "System", false);
+       fail_strne(layers[0].backend, "gdbm", false);
+       fail_strne(layers[0].description, "Operating System configuration layer", false);
+       fail_ne(layers[0].priority, 0);
+
+       fail_strne(layers[1].name, "isp", false);
+       fail_strne(layers[1].type, "System", false);
+       fail_strne(layers[1].backend, "gdbm", false);
+       fail_strne(layers[1].description, "ISP specific settings", false);
+       fail_ne(layers[1].priority, 1);
+
+       /* ... */
+
+       fail_strne(layers[6].name, "test-gdbm-user", false);
+       fail_strne(layers[6].type, "User", false);
+       fail_strne(layers[6].backend, "gdbm", false);
+       fail_strne(layers[6].description, "GDBM test db for user", false);
+       fail_ne(layers[6].priority, 6000);
+
+
+
+}
+END_TEST
+
+START_TEST(ini_parse_check)
+{
+       char ini_good[] = "test/test-pass.ini";
+       char ini_bad[] = "test/test-fail.ini";
+       dictionary *ini = NULL;
+
+       ini = iniparser_load(ini_good);
+       fail_if(ini == NULL,
+               "Failed to parse ini file");
+
+       iniparser_dump(ini, stdout);
+       iniparser_freedict(ini);
+
+       ini = iniparser_load(ini_bad);
+       fail_if(ini != NULL,
+               "Failed to catch bad ini file");
+
+       iniparser_dump(ini, stdout);
+       iniparser_freedict(ini);
+}
+END_TEST
+
+static Suite *
+configurator_suite(void)
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("configurator");
+
+       tc = tcase_create("compilation defaults");
+       tcase_add_test(tc, configurator_default_conf_file);
+       tcase_add_test(tc, configurator_default_module_dir);
+       tcase_add_test(tc, configurator_default_db_path);
+       tcase_add_test(tc, configurator_default_smack_load_file);
+       tcase_add_test(tc, configurator_default_buxton_socket);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("env clobbers defaults");
+       tcase_add_test(tc, configurator_env_conf_file);
+       tcase_add_test(tc, configurator_env_module_dir);
+       tcase_add_test(tc, configurator_env_db_path);
+       tcase_add_test(tc, configurator_env_smack_load_file);
+       tcase_add_test(tc, configurator_env_buxton_socket);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("command line clobbers all");
+       tcase_add_test(tc, configurator_cmd_conf_file);
+       tcase_add_test(tc, configurator_cmd_module_dir);
+       tcase_add_test(tc, configurator_cmd_db_path);
+       tcase_add_test(tc, configurator_cmd_smack_load_file);
+       tcase_add_test(tc, configurator_cmd_buxton_socket);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("config file works");
+       tcase_add_test(tc, configurator_conf_module_dir);
+       tcase_add_test(tc, configurator_conf_db_path);
+       tcase_add_test(tc, configurator_conf_smack_load_file);
+       tcase_add_test(tc, configurator_conf_buxton_socket);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("config file works");
+       tcase_add_test(tc, configurator_get_layers);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("ini_functions");
+       tcase_add_test(tc, ini_parse_check);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
+
+int main(void)
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+
+       s = configurator_suite();
+       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;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_daemon.c b/test/check_daemon.c
new file mode 100644 (file)
index 0000000..eaf253e
--- /dev/null
@@ -0,0 +1,2759 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <check.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "buxton.h"
+#include "buxtonresponse.h"
+#include "configurator.h"
+#include "check_utils.h"
+#include "daemon.h"
+#include "direct.h"
+#include "hashmap.h"
+#include "log.h"
+#include "smack.h"
+#include "util.h"
+
+#ifdef NDEBUG
+#error "re-run configure with --enable-debug"
+#endif
+
+#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK"
+
+static pid_t daemon_pid;
+int fuzz_time;
+
+typedef struct _fuzz_context_t {
+       uint8_t buf[4096];
+       size_t size;
+       int iteration;
+} FuzzContext;
+
+static bool use_smack(void)
+{
+       bool __attribute__((unused))dummy;
+
+       dummy = buxton_cache_smack_rules();
+
+       return buxton_smack_enabled();
+}
+
+static char* dump_fuzz(FuzzContext *fuzz)
+{
+       char* buf;
+       size_t buff_size;
+       FILE* s;
+       int l = 0;
+       int c = 0;
+
+       s = open_memstream(&buf, &buff_size);
+
+       fprintf(s, "\n\n******************************************\n");
+       fprintf(s, "current time %ld\n", time(NULL));
+       fprintf(s, "iteration: %d\tsize: %llu\n", fuzz->iteration, (unsigned long long)fuzz->size);
+       for (int i = 0; i < fuzz->size; i++) {
+               fprintf(s, "%02X ", fuzz->buf[i]);
+               c+= 3;
+               if (c > 80) {
+                       fprintf(s, "\n");
+                       c = 0;
+                       l++;
+               }
+       }
+       fclose(s);
+
+       return buf;
+}
+
+static void check_did_not_crash(pid_t pid, FuzzContext *fuzz)
+{
+       pid_t rpid;
+       int status;
+
+       rpid = waitpid(pid, &status, WNOHANG);
+       fail_if(rpid == -1, "couldn't wait for pid %m");
+       if (rpid == 0) {
+               /* child is still running */
+               return;
+       }
+       fail_if(WIFEXITED(status), "daemon exited with status %d%s",
+               WEXITSTATUS(status), dump_fuzz(fuzz));
+       fail_if(WIFSIGNALED(status), "daemon was killed with signal %d%s",
+               WTERMSIG(status), dump_fuzz(fuzz));
+}
+
+static void exec_daemon(void)
+{
+       char path[PATH_MAX];
+
+       //FIXME: path is wrong for makedistcheck
+       snprintf(path, PATH_MAX, "%s/check_buxtond", get_current_dir_name());
+
+       if (execl(path, "check_buxtond", (const char*)NULL) < 0) {
+               fail("couldn't exec: %m");
+       }
+       fail("should never reach here");
+}
+
+static void setup(void)
+{
+       daemon_pid = 0;
+       sigset_t sigset;
+       pid_t pid;
+
+       unlink(buxton_socket());
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+       pid = fork();
+       fail_if(pid < 0, "couldn't fork");
+       if (pid) {
+               /* parent*/
+               daemon_pid = pid;
+               usleep(128*1000);
+       } else {
+               /* child */
+               exec_daemon();
+       }
+}
+
+static void teardown(void)
+{
+       if (daemon_pid) {
+               int status;
+               pid_t pid;
+
+               pid = waitpid(daemon_pid, &status, WNOHANG);
+               fail_if(pid == -1, "waitpid error");
+               if (pid) {
+                       fail("daemon crashed!");
+               } else  {
+                       /* if the daemon is still running, kill it */
+                       kill(SIGTERM, daemon_pid);
+                       usleep(64*1000);
+                       kill(SIGKILL, daemon_pid);
+               }
+       }
+}
+
+START_TEST(buxton_open_check)
+{
+       BuxtonClient c = NULL;
+       fail_if(buxton_open(&c) == -1,
+               "Connection failed to open with daemon.");
+}
+END_TEST
+
+static void client_create_group_test(BuxtonResponse response, void *data)
+{
+       char *k = (char *)data;
+       BuxtonKey key;
+       char *group;
+       uid_t uid = getuid();
+       char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+       bool skip_check = (root_check && streq(root_check, "0"));
+
+       fail_if(buxton_response_type(response) != BUXTON_CONTROL_CREATE_GROUP,
+               "Failed to get create group response type");
+
+       if (uid == 0) {
+               fail_if(buxton_response_status(response) != 0,
+                       "Create group failed");
+               key = buxton_response_key(response);
+               fail_if(!key, "Failed to get create group key");
+               group = buxton_key_get_group(key);
+               fail_if(!group, "Failed to get group from key");
+               fail_if(!streq(group, k),
+                       "Incorrect set key returned");
+               free(group);
+               buxton_key_free(key);
+       } else {
+               fail_if(buxton_response_status(response) == 0  && !skip_check,
+                       "Create group succeeded, but the client is not root");
+       }
+}
+START_TEST(buxton_create_group_check)
+{
+       BuxtonClient c;
+       BuxtonKey key = buxton_key_create("tgroup", NULL, "base", STRING);
+       fail_if(!key, "Failed to create key");
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+       fail_if(buxton_create_group(c, key, client_create_group_test,
+                                   "tgroup", true),
+               "Creating group in buxton failed.");
+       buxton_key_free(key);
+}
+END_TEST
+
+static void client_remove_group_test(BuxtonResponse response, void *data)
+{
+       char *k = (char *)data;
+       BuxtonKey key;
+       char *group;
+       uid_t uid = getuid();
+       char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+       bool skip_check = (root_check && streq(root_check, "0"));
+
+       fail_if(buxton_response_type(response) != BUXTON_CONTROL_REMOVE_GROUP,
+               "Failed to get remove group response type");
+
+       if (uid == 0) {
+               fail_if(buxton_response_status(response) != 0,
+                       "Remove group failed");
+               key = buxton_response_key(response);
+               fail_if(!key, "Failed to get create group key");
+               group = buxton_key_get_group(key);
+               fail_if(!group, "Failed to get group from key");
+               fail_if(!streq(group, k),
+                       "Incorrect set key returned");
+               free(group);
+               buxton_key_free(key);
+       } else {
+               fail_if(buxton_response_status(response) == 0  && !skip_check,
+                       "Create group succeeded, but the client is not root");
+       }
+}
+START_TEST(buxton_remove_group_check)
+{
+       BuxtonClient c;
+       BuxtonKey key = buxton_key_create("tgroup", NULL, "base", STRING);
+       fail_if(!key, "Failed to create key");
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+       fail_if(buxton_remove_group(c, key, client_remove_group_test,
+                                   "tgroup", true),
+               "Removing group in buxton failed.");
+       buxton_key_free(key);
+}
+END_TEST
+
+static void client_set_value_test(BuxtonResponse response, void *data)
+{
+       char *k = (char *)data;
+       BuxtonKey key;
+       char *group;
+
+       fail_if(buxton_response_type(response) != BUXTON_CONTROL_SET,
+               "Failed to get set response type");
+       fail_if(buxton_response_status(response) != 0,
+               "Set value failed");
+       key = buxton_response_key(response);
+       fail_if(!key, "Failed to get set key");
+       group = buxton_key_get_group(key);
+       fail_if(!group, "Failed to get group from key");
+       fail_if(!streq(group, k),
+               "Incorrect set group returned");
+       free(group);
+       buxton_key_free(key);
+}
+START_TEST(buxton_set_value_check)
+{
+       BuxtonClient c;
+       BuxtonKey group = buxton_key_create("group", NULL, "test-gdbm-user", STRING);
+       fail_if(!group, "Failed to create key for group");
+       BuxtonKey key = buxton_key_create("group", "name", "test-gdbm-user", STRING);
+       fail_if(!key, "Failed to create key");
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+       fail_if(buxton_create_group(c, group, NULL, NULL, true),
+               "Creating group in buxton failed.");
+       fail_if(buxton_set_label(c, group, "*", NULL, NULL, true),
+               "Setting group in buxton failed.");
+       fail_if(buxton_set_value(c, key, "bxt_test_value",
+                                client_set_value_test,
+                                "group", true),
+               "Setting value in buxton failed.");
+       buxton_key_free(group);
+       buxton_key_free(key);
+}
+END_TEST
+
+static void client_set_label_test(BuxtonResponse response, void *data)
+{
+       BuxtonKey user_key = (BuxtonKey)data;
+       BuxtonKey key;
+       char *user_group, *group;
+       char *user_name, *name;
+       uid_t uid = getuid();
+       char *root_check = getenv(BUXTON_ROOT_CHECK_ENV);
+       bool skip_check = (root_check && streq(root_check, "0"));
+
+       fail_if(buxton_response_type(response) != BUXTON_CONTROL_SET_LABEL,
+               "Failed to get set label response type");
+
+       if (uid == 0) {
+               fail_if(buxton_response_status(response) != 0,
+                       "Set label failed");
+               key = buxton_response_key(response);
+               fail_if(!key, "Failed to get set label key");
+               user_group = buxton_key_get_group(user_key);
+               fail_if(!user_group, "Failed to get group from user key");
+               group = buxton_key_get_group(key);
+               fail_if(!group, "Failed to get group from key");
+               fail_if(!streq(group, user_group),
+                       "Incorrect set label group returned");
+               free(user_group);
+               free(group);
+
+               user_name = buxton_key_get_name(user_key);
+               if (user_name) {
+                       name = buxton_key_get_name(key);
+                       fail_if(!name, "Failed to get name from key");
+                       fail_if(!streq(name, user_name),
+                               "Incorrect set label name returned");
+                       free(user_name);
+                       free(name);
+               }
+               buxton_key_free(key);
+       } else {
+               if (skip_check) {
+                       fail_if(buxton_response_status(response) != 0,
+                       "Set label failed");
+               } else {
+                       fail_if(buxton_response_status(response) == 0,
+                       "Set label succeeded, but the client is not root");
+               }
+       }
+}
+START_TEST(buxton_set_label_check)
+{
+       BuxtonClient c;
+       BuxtonKey group = buxton_key_create("bxt_group", NULL, "test-gdbm", STRING);
+       fail_if(!group, "Failed to create key for group");
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+       fail_if(buxton_create_group(c, group, NULL, NULL, true),
+               "Creating group in buxton failed.");
+       fail_if(buxton_set_label(c, group, "*",
+                                client_set_label_test,
+                                group, true),
+               "Setting label for group in buxton failed.");
+
+       BuxtonKey name = buxton_key_create("bxt_group", "bxt_name", "test-gdbm", STRING);
+       fail_if(!name, "Failed to create key for name");
+       fail_if(buxton_set_value(c, name, "bxt_value", NULL, NULL, true),
+               "Setting label for name in buxton failed.");
+       fail_if(buxton_set_label(c, name, "*",
+                                client_set_label_test,
+                                name, true),
+               "Setting label for name in buxton failed.");
+
+       buxton_key_free(group);
+       buxton_key_free(name);
+}
+END_TEST
+
+static void client_get_value_test(BuxtonResponse response, void *data)
+{
+       BuxtonKey key;
+       char *group;
+       char *name;
+       char *v;
+       char *value = (char *)data;
+
+       fail_if(buxton_response_status(response) != 0,
+               "Get value failed");
+
+       key = buxton_response_key(response);
+       fail_if(!key, "Failed to get key");
+       group = buxton_key_get_group(key);
+       fail_if(!group, "Failed to get group");
+       fail_if(!streq(group, "group"),
+               "Failed to get correct group");
+       name = buxton_key_get_name(key);
+       fail_if(!name, "Failed to get name");
+       fail_if(!streq(name, "name"),
+               "Failed to get correct name");
+       v = buxton_response_value(response);
+       printf("val=%s\n", v);
+       fail_if(!v, "Failed to get value");
+       fail_if(!streq(v, value),
+               "Failed to get correct value");
+
+       free(v);
+       free(group);
+       free(name);
+       buxton_key_free(key);
+}
+START_TEST(buxton_get_value_for_layer_check)
+{
+       BuxtonClient c = NULL;
+       BuxtonKey key = buxton_key_create("group", "name", "test-gdbm-user", STRING);
+
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+       fail_if(buxton_get_value(c, key,
+                                client_get_value_test,
+                                "bxt_test_value", true),
+               "Retrieving value from buxton gdbm backend failed.");
+}
+END_TEST
+
+START_TEST(buxton_get_value_check)
+{
+       BuxtonClient c = NULL;
+
+       BuxtonKey group = buxton_key_create("group", NULL, "test-gdbm", STRING);
+       fail_if(!group, "Failed to create key for group");
+       BuxtonKey key = buxton_key_create("group", "name", "test-gdbm", STRING);
+
+       fail_if(buxton_open(&c) == -1,
+               "Open failed with daemon.");
+
+       fail_if(buxton_create_group(c, group, NULL, NULL, true),
+               "Creating group in buxton failed.");
+       fail_if(buxton_set_label(c, group, "*", NULL, NULL, true),
+               "Setting group in buxton failed.");
+       fail_if(buxton_set_value(c, key, "bxt_test_value2",
+                                client_set_value_test, "group", true),
+               "Failed to set second value.");
+       buxton_key_free(group);
+       buxton_key_free(key);
+       key = buxton_key_create("group", "name", NULL, STRING);
+       fail_if(buxton_get_value(c, key,
+                                client_get_value_test,
+                                "bxt_test_value2", true),
+               "Retrieving value from buxton gdbm backend failed.");
+       buxton_key_free(key);
+}
+END_TEST
+
+START_TEST(parse_list_check)
+{
+       BuxtonData l3[2];
+       BuxtonData l2[4];
+       BuxtonData l1[3];
+       _BuxtonKey key;
+       BuxtonData *value = NULL;
+
+       fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 2, l1, &key, &value),
+               "Parsed bad notify argument count");
+       l1[0].type = INT32;
+       l1[1].type = STRING;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value),
+               "Parsed bad notify type 1");
+       l1[0].type = STRING;
+       l1[1].type = FLOAT;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value),
+               "Parsed bad notify type 3");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value),
+               "Parsed bad notify type 3");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = UINT32;
+       l1[0].store.d_string = buxton_string_pack("s1");
+       l1[1].store.d_string = buxton_string_pack("s2");
+       l1[2].store.d_uint32 = STRING;
+       fail_if(!parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value),
+               "Unable to parse valid notify");
+       fail_if(!streq(key.group.value, l1[0].store.d_string.value),
+               "Failed to set correct notify group");
+       fail_if(!streq(key.name.value, l1[1].store.d_string.value),
+               "Failed to set correct notify name");
+       fail_if(key.type != l1[2].store.d_uint32,
+               "Failed to set correct notify type");
+
+       fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 2, l1, &key, &value),
+               "Parsed bad unnotify argument count");
+       l1[0].type = INT32;
+       l1[1].type = STRING;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value),
+               "Parsed bad unnotify type 1");
+       l1[0].type = STRING;
+       l1[1].type = FLOAT;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value),
+               "Parsed bad unnotify type 2");
+       l1[0].type = INT32;
+       l1[1].type = STRING;
+       l1[2].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value),
+               "Parsed bad unnotify type 3");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = UINT32;
+       l1[0].store.d_string = buxton_string_pack("s3");
+       l1[1].store.d_string = buxton_string_pack("s4");
+       l1[2].store.d_uint32 = STRING;
+       fail_if(!parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value),
+               "Unable to parse valid unnotify");
+       fail_if(!streq(key.group.value, l1[0].store.d_string.value),
+               "Failed to set correct unnotify group");
+       fail_if(!streq(key.name.value, l1[1].store.d_string.value),
+               "Failed to set correct unnotify name");
+       fail_if(key.type != l1[2].store.d_uint32,
+               "Failed to set correct unnotify type");
+
+       fail_if(parse_list(BUXTON_CONTROL_GET, 5, l2, &key, &value),
+               "Parsed bad get argument count");
+       l2[0].type = INT32;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value),
+               "Parsed bad get type 1");
+       l2[0].type = STRING;
+       l2[1].type = FLOAT;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value),
+               "Parsed bad get type 2");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = BOOLEAN;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value),
+               "Parsed bad get type 3");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value),
+               "Parsed bad get type 4");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       l2[0].store.d_string = buxton_string_pack("s5");
+       l2[1].store.d_string = buxton_string_pack("s6");
+       l2[2].store.d_string = buxton_string_pack("s7");
+       l2[3].store.d_uint32 = STRING;
+       fail_if(!parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value),
+               "Unable to parse valid get 1");
+       fail_if(!streq(key.layer.value, l2[0].store.d_string.value),
+               "Failed to set correct get layer 1");
+       fail_if(!streq(key.group.value, l2[1].store.d_string.value),
+               "Failed to set correct get group 1");
+       fail_if(!streq(key.name.value, l2[2].store.d_string.value),
+               "Failed to set correct get name");
+       fail_if(key.type != l2[3].store.d_uint32,
+               "Failed to set correct get type 1");
+       l2[0].store.d_string = buxton_string_pack("s6");
+       l2[1].store.d_string = buxton_string_pack("s6");
+       l2[2].type = UINT32;
+       l2[2].store.d_uint32 = STRING;
+       fail_if(!parse_list(BUXTON_CONTROL_GET, 3, l2, &key, &value),
+               "Unable to parse valid get 2");
+       fail_if(!streq(key.group.value, l2[0].store.d_string.value),
+               "Failed to set correct get group 2");
+       fail_if(!streq(key.name.value, l2[1].store.d_string.value),
+               "Failed to set correct get name 2");
+       fail_if(key.type != l2[2].store.d_uint32,
+               "Failed to set correct get type 2");
+       l1[0].type = INT32;
+       l1[1].type = STRING;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value),
+               "Parsed bad get type 5");
+       l1[0].type = STRING;
+       l1[1].type = FLOAT;
+       l1[2].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value),
+               "Parsed bad get type 6");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = BOOLEAN;
+       fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value),
+               "Parsed bad get type 7");
+
+       fail_if(parse_list(BUXTON_CONTROL_SET, 1, l2, &key, &value),
+               "Parsed bad set argument count");
+       l2[0].type = INT32;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = FLOAT;
+       fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value),
+               "Parsed bad set type 1");
+       l2[0].type = STRING;
+       l2[1].type = FLOAT;
+       l2[2].type = STRING;
+       l2[3].type = FLOAT;
+       fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value),
+               "Parsed bad set type 2");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = BOOLEAN;
+       l2[3].type = FLOAT;
+       fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value),
+               "Parsed bad set type 3");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = FLOAT;
+       l2[0].store.d_string = buxton_string_pack("s8");
+       l2[1].store.d_string = buxton_string_pack("s9");
+       l2[2].store.d_string = buxton_string_pack("s10");
+       l2[3].store.d_float = 3.14F;
+       fail_if(!parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value),
+               "Unable to parse valid set 1");
+       fail_if(!streq(key.layer.value, l2[0].store.d_string.value),
+               "Failed to set correct set layer 1");
+       fail_if(!streq(key.group.value, l2[1].store.d_string.value),
+               "Failed to set correct set group 1");
+       fail_if(!streq(key.name.value, l2[2].store.d_string.value),
+               "Failed to set correct set name 1");
+       fail_if(value->store.d_float != l2[3].store.d_float,
+               "Failed to set correct set value 1");
+
+       fail_if(parse_list(BUXTON_CONTROL_UNSET, 1, l2, &key, &value),
+               "Parsed bad unset argument count");
+       l2[0].type = INT32;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value),
+               "Parsed bad unset type 1");
+       l2[0].type = STRING;
+       l2[1].type = FLOAT;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value),
+               "Parsed bad unset type 2");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = BOOLEAN;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value),
+               "Parsed bad unset type 3");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value),
+               "Parsed bad unset type 4");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       l2[0].store.d_string = buxton_string_pack("s11");
+       l2[1].store.d_string = buxton_string_pack("s12");
+       l2[2].store.d_string = buxton_string_pack("s13");
+       l2[3].store.d_uint32 = STRING;
+       fail_if(!parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value),
+               "Unable to parse valid unset 1");
+       fail_if(!streq(key.layer.value, l2[0].store.d_string.value),
+               "Failed to set correct unset layer 1");
+       fail_if(!streq(key.group.value, l2[1].store.d_string.value),
+               "Failed to set correct unset group 1");
+       fail_if(!streq(key.name.value, l2[2].store.d_string.value),
+               "Failed to set correct unset name 1");
+       fail_if(key.type != l2[3].store.d_uint32,
+               "Failed to set correct unset type 1");
+
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 1, l2, &key, &value),
+               "Parsed bad set label argument count");
+       l1[0].type = INT32;
+       l1[1].type = STRING;
+       l1[2].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value),
+               "Parsed bad set label type 1");
+       l1[0].type = STRING;
+       l1[1].type = FLOAT;
+       l1[2].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value),
+               "Parsed bad set label type 2");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = BOOLEAN;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value),
+               "Parsed bad set label type 3");
+       l1[0].type = STRING;
+       l1[1].type = STRING;
+       l1[2].type = STRING;
+       l1[0].store.d_string = buxton_string_pack("s14");
+       l1[1].store.d_string = buxton_string_pack("s15");
+       l1[2].store.d_string = buxton_string_pack("*");
+       fail_if(!parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value),
+               "Unable to parse valid set label 1");
+       fail_if(!streq(key.layer.value, l1[0].store.d_string.value),
+               "Failed to set correct set label layer 1");
+       fail_if(!streq(key.group.value, l1[1].store.d_string.value),
+               "Failed to set correct set label group 1");
+       fail_if(!streq(value->store.d_string.value, l1[2].store.d_string.value),
+               "Failed to set correct set label label 1");
+       fail_if(key.type != STRING, "Failed to key type in set label");
+       l2[0].type = INT32;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value),
+               "Parsed bad set label type 4");
+       l2[0].type = STRING;
+       l2[1].type = FLOAT;
+       l2[2].type = STRING;
+       l2[3].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value),
+               "Parsed bad set label type 5");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = BOOLEAN;
+       l2[3].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value),
+               "Parsed bad set label type 6");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = UINT32;
+       fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value),
+               "Parsed bad set label type 7");
+       l2[0].type = STRING;
+       l2[1].type = STRING;
+       l2[2].type = STRING;
+       l2[3].type = STRING;
+       l2[0].store.d_string = buxton_string_pack("x1");
+       l2[1].store.d_string = buxton_string_pack("x2");
+       l2[2].store.d_string = buxton_string_pack("x3");
+       l2[3].store.d_string = buxton_string_pack("x4");
+       fail_if(!parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value),
+               "Unable to parse valid set label 2");
+       fail_if(!streq(key.layer.value, l2[0].store.d_string.value),
+               "Failed to set correct set label layer 2");
+       fail_if(!streq(key.group.value, l2[1].store.d_string.value),
+               "Failed to set correct set label group 2");
+       fail_if(!streq(key.name.value, l2[2].store.d_string.value),
+               "Failed to set correct set label name 2");
+       fail_if(!streq(value->store.d_string.value, l2[3].store.d_string.value),
+               "Failed to set correct set label label 2");
+
+       fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 1, l3, &key, &value),
+               "Parsed bad create group argument count");
+       l3[0].type = INT32;
+       l3[1].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value),
+               "Parsed bad create group type 1");
+       l3[0].type = STRING;
+       l3[1].type = FLOAT;
+       fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value),
+               "Parsed bad create group type 2");
+       l3[0].type = STRING;
+       l3[1].type = STRING;
+       l3[0].store.d_string = buxton_string_pack("s16");
+       l3[1].store.d_string = buxton_string_pack("s17");
+       fail_if(!parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value),
+               "Unable to parse valid create group 1");
+       fail_if(!streq(key.layer.value, l3[0].store.d_string.value),
+               "Failed to set correct create group layer 1");
+       fail_if(!streq(key.group.value, l3[1].store.d_string.value),
+               "Failed to set correct create group group 1");
+       fail_if(key.type != STRING, "Failed to key type in create group");
+
+       fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 1, l3, &key, &value),
+               "Parsed bad remove group argument count");
+       l3[0].type = INT32;
+       l3[1].type = STRING;
+       fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value),
+               "Parsed bad remove group type 1");
+       l3[0].type = STRING;
+       l3[1].type = FLOAT;
+       fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value),
+               "Parsed bad remove group type 2");
+       l3[0].type = STRING;
+       l3[1].type = STRING;
+       l3[0].store.d_string = buxton_string_pack("s18");
+       l3[1].store.d_string = buxton_string_pack("s19");
+       fail_if(!parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value),
+               "Unable to parse valid remove group 1");
+       fail_if(!streq(key.layer.value, l3[0].store.d_string.value),
+               "Failed to set correct remove group layer 1");
+       fail_if(!streq(key.group.value, l3[1].store.d_string.value),
+               "Failed to set correct remove group group 1");
+       fail_if(key.type != STRING, "Failed to key type in remove group");
+
+       fail_if(parse_list(BUXTON_CONTROL_MIN, 2, l3, &key, &value),
+               "Parsed bad control type 1");
+}
+END_TEST
+
+START_TEST(create_group_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       client_list_item client;
+       int32_t status;
+       BuxtonDaemon server;
+       BuxtonString clabel = buxton_string_pack("_");
+
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+
+       client.cred.uid = getuid();
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+       server.buxton.client.uid = 0;
+
+       key.layer = buxton_string_pack("test-gdbm-user");
+       key.group = buxton_string_pack("daemon-check");
+       key.type = STRING;
+       create_group(&server, &client, &key, &status);
+       fail_if(status != 0, "Failed to create group");
+
+       key.layer = buxton_string_pack("test-gdbm");
+       create_group(&server, &client, &key, &status);
+       fail_if(status != 0, "Failed to create group");
+
+       key.layer = buxton_string_pack("base");
+       key.group = buxton_string_pack("tgroup");
+       create_group(&server, &client, &key, &status);
+       fail_if(status != 0, "Failed to create group");
+
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+
+START_TEST(remove_group_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       client_list_item client;
+       int32_t status;
+       BuxtonDaemon server;
+       BuxtonString clabel = buxton_string_pack("_");
+
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+
+       client.cred.uid = getuid();
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+       server.buxton.client.uid = 0;
+
+       key.layer = buxton_string_pack("base");
+       key.group = buxton_string_pack("tgroup");
+       key.type = STRING;
+
+       remove_group(&server, &client, &key, &status);
+       fail_if(status != 0, "Failed to remove group");
+
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+
+START_TEST(set_label_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       BuxtonData value;
+       client_list_item client;
+       int32_t status;
+       BuxtonDaemon server;
+       BuxtonString clabel = buxton_string_pack("_");
+
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+
+       client.cred.uid = getuid();
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+       server.buxton.client.uid = 0;
+       key.layer = buxton_string_pack("test-gdbm");
+       key.group = buxton_string_pack("daemon-check");
+       key.type = STRING;
+       value.type = STRING;
+       value.store.d_string = buxton_string_pack("*");
+
+       set_label(&server, &client, &key, &value, &status);
+       fail_if(status != 0, "Failed to set label");
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+
+START_TEST(set_value_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       BuxtonData value;
+       client_list_item client;
+       int32_t status;
+       BuxtonDaemon server;
+       BuxtonString clabel = buxton_string_pack("_");
+
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+
+       client.cred.uid = getuid();
+       server.buxton.client.uid = 0;
+
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+
+       key.layer = buxton_string_pack("test-gdbm-user");
+       key.group = buxton_string_pack("daemon-check");
+       key.name = buxton_string_pack("name");
+       value.type = STRING;
+       value.store.d_string = buxton_string_pack("user-layer-value");
+
+       set_value(&server, &client, &key, &value, &status);
+       fail_if(status != 0, "Failed to set value");
+       fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid");
+
+       key.layer = buxton_string_pack("test-gdbm");
+       value.store.d_string = buxton_string_pack("system-layer-value");
+       set_value(&server, &client, &key, &value, &status);
+       fail_if(status != 0, "Failed to set value");
+
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+
+START_TEST(get_value_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       BuxtonData *value;
+       client_list_item client;
+       int32_t status;
+       BuxtonDaemon server;
+       BuxtonString clabel = buxton_string_pack("_");
+
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+
+       fail_if(!buxton_cache_smack_rules(),
+               "Failed to cache smack rules");
+       client.cred.uid = getuid();
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+       server.buxton.client.uid = 0;
+       key.layer = buxton_string_pack("test-gdbm-user");
+       key.group = buxton_string_pack("daemon-check");
+       key.name = buxton_string_pack("name");
+       key.type = STRING;
+
+       value = get_value(&server, &client, &key, &status);
+       fail_if(!value, "Failed to get value");
+       fail_if(status != 0, "Failed to get value");
+       fail_if(value->type != STRING, "Failed to get correct type");
+       fail_if(!streq(value->store.d_string.value, "user-layer-value"), "Failed to get correct value");
+       fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid");
+       free(value);
+
+       server.buxton.client.uid = 0;
+       key.layer.value = NULL;
+       key.layer.length = 0;
+       value = get_value(&server, &client, &key, &status);
+       fail_if(!value, "Failed to get value 2");
+       fail_if(status != 0, "Failed to get value 2");
+       fail_if(value->type != STRING, "Failed to get correct type 2");
+       fail_if(!streq(value->store.d_string.value, "system-layer-value"), "Failed to get correct value 2");
+       fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid 2");
+       free(value);
+
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+
+START_TEST(register_notification_check)
+{
+       _BuxtonKey key = { {0}, {0}, {0}, 0};
+       client_list_item client, no_client;
+       BuxtonString clabel = buxton_string_pack("_");
+       int32_t status;
+       BuxtonDaemon server;
+       uint32_t msgid;
+
+       fail_if(!buxton_cache_smack_rules(),
+               "Failed to cache smack rules");
+       if (use_smack())
+               client.smack_label = &clabel;
+       else
+               client.smack_label = NULL;
+       client.cred.uid = 1002;
+       fail_if(!buxton_direct_open(&server.buxton),
+               "Failed to open buxton direct connection");
+       server.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!server.notify_mapping, "Failed to allocate hashmap");
+
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name");
+       key.type = STRING;
+       register_notification(&server, &client, &key, 1, &status);
+       fail_if(status != 0, "Failed to register notification");
+       register_notification(&server, &client, &key, 1, &status);
+       fail_if(status != 0, "Failed to register notification");
+       //FIXME: Figure out what to do with duplicates
+       key.group = buxton_string_pack("no-key");
+       msgid = unregister_notification(&server, &client, &key, &status);
+       fail_if(status == 0,
+               "Unregistered from notifications with invalid key");
+       fail_if(msgid != 0, "Got unexpected notify message id");
+       key.group = buxton_string_pack("group");
+       msgid = unregister_notification(&server, &no_client, &key, &status);
+       fail_if(status == 0,
+               "Unregistered from notifications with invalid client");
+       fail_if(msgid != 0, "Got unexpected notify message id");
+       msgid = unregister_notification(&server, &client, &key, &status);
+       fail_if(status != 0,
+               "Unable to unregister from notifications");
+       fail_if(msgid != 1, "Failed to get correct notify message id");
+       key.group = buxton_string_pack("key2");
+       register_notification(&server, &client, &key, 0, &status);
+       fail_if(status == 0, "Registered notification with key not in db");
+
+       hashmap_free(server.notify_mapping);
+       buxton_direct_close(&server.buxton);
+}
+END_TEST
+START_TEST(buxtond_handle_message_error_check)
+{
+       int client, server;
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1;
+       client_list_item cl;
+       bool r;
+       BuxtonArray *list = NULL;
+       uint16_t control;
+
+       setup_socket_pair(&client, &server);
+       fail_if(fcntl(client, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       list = buxton_array_new();
+       fail_if(!list, "Failed to allocate list");
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       cl.smack_label = &slabel;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+
+       cl.data = malloc(4);
+       fail_if(!cl.data, "Couldn't allocate blank message");
+       cl.data[0] = 0;
+       cl.data[1]= 0;
+       cl.data[2] = 0;
+       cl.data[3] = 0;
+       size = 100;
+       r = buxtond_handle_message(&daemon, &cl, size);
+       fail_if(r, "Failed to detect invalid message data");
+       free(cl.data);
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("group");
+       r = buxton_array_add(list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0,
+                                       list);
+       fail_if(size == 0, "Failed to serialize message");
+       control = BUXTON_CONTROL_MIN;
+       memcpy(cl.data, &control, sizeof(uint16_t));
+       r = buxtond_handle_message(&daemon, &cl, size);
+       fail_if(r, "Failed to detect min control size");
+       control = BUXTON_CONTROL_MAX;
+       memcpy(cl.data, &control, sizeof(uint16_t));
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(r, "Failed to detect max control size");
+
+       close(client);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_create_group_check)
+{
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list1, *out_list2;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       int client, server;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       fail_if(fcntl(client, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+
+       out_list1 = buxton_array_new();
+       fail_if(!out_list1, "Failed to allocate list");
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("base");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("tgroup");
+       r = buxton_array_add(out_list1, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list1, &data2);
+       fail_if(!r, "Failed to add element to array");
+
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_CREATE_GROUP, 0,
+                                       out_list1);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to handle create group message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to create group");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to create group");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       free(list);
+
+       out_list2 = buxton_array_new();
+       fail_if(!out_list2, "Failed to allocate list");
+       data1.store.d_string = buxton_string_pack("base");
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       r = buxton_array_add(out_list2, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list2, &data2);
+       fail_if(!r, "Failed to add element to array");
+
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_CREATE_GROUP, 1,
+                                       out_list2);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to handle create group message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to create group");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to create group");
+       fail_if(msgid != 1, "Failed to get correct message id");
+
+       free(list);
+       cleanup_callbacks();
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list1, NULL);
+       buxton_array_free(&out_list2, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_remove_group_check)
+{
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       int client, server;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       fail_if(fcntl(client, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("base");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("tgroup");
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_REMOVE_GROUP, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to handle remove group message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to remove group");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to remove group");
+       fail_if(msgid != 0, "Failed to get correct message id");
+
+       free(list);
+       cleanup_callbacks();
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_set_label_check)
+{
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2, data3;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       int client, server;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       fail_if(fcntl(client, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("base");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       data3.type = STRING;
+       data3.store.d_string = buxton_string_pack("*");
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data3);
+       fail_if(!r, "Failed to add element to array");
+
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_SET_LABEL, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to handle set label message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to set label");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to set label");
+       fail_if(msgid != 0, "Failed to get correct message id");
+
+       free(list);
+       cleanup_callbacks();
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_set_value_check)
+{
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2, data3, data4;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       int client, server;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       fail_if(fcntl(client, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(server, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("base");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       data3.type = STRING;
+       data3.store.d_string = buxton_string_pack("name");
+       data4.type = STRING;
+       data4.store.d_string = buxton_string_pack("bxt_test_value3");
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data3);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data4);
+       fail_if(!r, "Failed to add element to array");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(r, "Failed to detect parse_list failure");
+
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_SET, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to handle set message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to set");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to set");
+       fail_if(msgid != 0, "Failed to get correct message id");
+
+       free(list);
+       cleanup_callbacks();
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_get_check)
+{
+       int client, server;
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2, data3, data4;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonArray *out_list2;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = getuid();
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("test-gdbm-user");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       data3.type = STRING;
+       data3.store.d_string = buxton_string_pack("name");
+       data4.type = UINT32;
+       data4.store.d_uint32 = STRING;
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data3);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data4);
+       fail_if(!r, "Failed to add element to array");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to get message 1");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 2, "Failed to get valid message from buffer");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != INT32, "Failed to get correct response type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to get value");
+       fail_if(list[1].type != STRING, "Failed to get correct value type");
+       fail_if(!streq(list[1].store.d_string.value, "user-layer-value"),
+               "Failed to get correct value");
+
+       free(list[1].store.d_string.value);
+       free(list);
+
+       out_list2 = buxton_array_new();
+       fail_if(!out_list2, "Failed to allocate list 2");
+       r = buxton_array_add(out_list2, &data2);
+       fail_if(!r, "Failed to add element to array 2");
+       r = buxton_array_add(out_list2, &data3);
+       fail_if(!r, "Failed to add element to array 2");
+       r = buxton_array_add(out_list2, &data4);
+       fail_if(!r, "Failed to add element to array 2");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET, 0,
+                                       out_list2);
+       fail_if(size == 0, "Failed to serialize message 2");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to get message 2");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed 2");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 2, "Failed to get correct response to get 2");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type 2");
+       fail_if(msgid != 0, "Failed to get correct message id 2");
+       fail_if(list[0].type != INT32, "Failed to get correct response type 2");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to get value 2");
+       fail_if(list[1].type != STRING, "Failed to get correct value type 2");
+       fail_if(streq(list[1].store.d_string.value, "bxt_test_value2"),
+               "Failed to get correct value 2");
+
+       free(list[1].store.d_string.value);
+       free(list);
+       close(client);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+       buxton_array_free(&out_list2, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_notify_check)
+{
+       int client, server;
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2, data3;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("group");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("name");
+       data3.type = UINT32;
+       data3.store.d_uint32 = STRING;
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data3);
+       fail_if(!r, "Failed to add element to array");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to register for notification");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to notify");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct notify message id");
+       fail_if(list[0].type != INT32, "Failed to get correct response type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to register notification");
+
+       free(list);
+
+       /* UNNOTIFY */
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_UNNOTIFY, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to unregister from notification");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed 2");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 2, "Failed to get correct response to unnotify");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type 2");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type 2");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to unregister for notification");
+       fail_if(list[1].type != UINT32,
+               "Failed to get correct unnotify message id type");
+       fail_if(list[1].store.d_uint32 != 0,
+               "Failed to get correct unnotify message id");
+       fail_if(msgid != 0, "Failed to get correct message id 2");
+
+       free(list);
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_handle_message_unset_check)
+{
+       int client, server;
+       BuxtonDaemon daemon;
+       BuxtonString slabel;
+       size_t size;
+       BuxtonData data1, data2, data3, data4;
+       client_list_item cl;
+       bool r;
+       BuxtonData *list;
+       BuxtonArray *out_list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+       out_list = buxton_array_new();
+       fail_if(!out_list, "Failed to allocate list");
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.buxton.client.uid = 1001;
+       fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+       daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("base");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       data3.type = STRING;
+       data3.store.d_string = buxton_string_pack("name");
+       data4.type = UINT32;
+       data4.store.d_uint32 = STRING;
+       r = buxton_array_add(out_list, &data1);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data2);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data3);
+       fail_if(!r, "Failed to add element to array");
+       r = buxton_array_add(out_list, &data4);
+       fail_if(!r, "Failed to add element to array");
+       size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_UNSET, 0,
+                                       out_list);
+       fail_if(size == 0, "Failed to serialize message");
+       r = buxtond_handle_message(&daemon, &cl, size);
+       free(cl.data);
+       fail_if(!r, "Failed to unset message");
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1, "Failed to get correct response to unset");
+       fail_if(msg != BUXTON_CONTROL_STATUS,
+               "Failed to get correct control type");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct indicator type");
+       fail_if(list[0].store.d_int32 != 0,
+               "Failed to unset");
+       fail_if(msgid != 0, "Failed to get correct message id");
+
+       free(list);
+       close(client);
+       hashmap_free(daemon.notify_mapping);
+       buxton_direct_close(&daemon.buxton);
+       buxton_array_free(&out_list, NULL);
+}
+END_TEST
+
+START_TEST(buxtond_notify_clients_check)
+{
+       int client, server;
+       BuxtonDaemon daemon;
+       _BuxtonKey key;
+       BuxtonString slabel;
+       BuxtonData value1, value2;
+       client_list_item cl;
+       int32_t status;
+       bool r;
+       BuxtonData *list;
+       BuxtonControlMessage msg;
+       ssize_t csize;
+       ssize_t s;
+       uint8_t buf[4096];
+       uint32_t msgid;
+
+       setup_socket_pair(&client, &server);
+
+       cl.fd = server;
+       slabel = buxton_string_pack("_");
+       if (use_smack())
+               cl.smack_label = &slabel;
+       else
+               cl.smack_label = NULL;
+       cl.cred.uid = 1002;
+       daemon.notify_mapping = hashmap_new(string_hash_func,
+                                           string_compare_func);
+       fail_if(!daemon.notify_mapping, "Failed to allocate hashmap");
+       fail_if(!buxton_cache_smack_rules(),
+               "Failed to cache Smack rules");
+       fail_if(!buxton_direct_open(&daemon.buxton),
+               "Failed to open buxton direct connection");
+
+       value1.type = STRING;
+       value1.store.d_string = buxton_string_pack("dummy value");
+       key.group = buxton_string_pack("dummy");
+       key.name = buxton_string_pack("name");
+       buxtond_notify_clients(&daemon, &cl, &key, &value1);
+
+       value1.store.d_string = buxton_string_pack("real value");
+       key.group = buxton_string_pack("daemon-check");
+       key.name = buxton_string_pack("name");
+       key.layer = buxton_string_pack("base");
+       key.type = STRING;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value1);
+
+       value2.type = STRING;
+       value2.store.d_string = buxton_string_pack("new value");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify string");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != STRING,
+               "Failed to get correct notification value type string");
+       fail_if(!streq(list[0].store.d_string.value, "new value"),
+               "Failed to get correct notification value data string");
+
+       free(list[0].store.d_string.value);
+       free(list);
+
+       key.group = buxton_string_pack("group");
+       key.name.value = NULL;
+       key.name.length = 0;
+       r = buxton_direct_create_group(&daemon.buxton, &key, NULL);
+       fail_if(!r, "Unable to create group");
+       r = buxton_direct_set_label(&daemon.buxton, &key, &slabel);
+       fail_if(!r, "Unable set group label");
+
+       value1.type = INT32;
+       value1.store.d_int32 = 1;
+       value2.type = INT32;
+       value2.store.d_int32 = 2;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name32");
+       key.type = INT32;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify int32");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != INT32,
+               "Failed to get correct notification value type int32");
+       fail_if(list[0].store.d_int32 != 2,
+               "Failed to get correct notification value data int32");
+
+       free(list);
+
+       value1.type = UINT32;
+       value1.store.d_uint32 = 1;
+       value2.type = UINT32;
+       value2.store.d_uint32 = 2;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("nameu32");
+       key.type = UINT32;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify uint32");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != UINT32,
+               "Failed to get correct notification value type uint32");
+       fail_if(list[0].store.d_uint32 != 2,
+               "Failed to get correct notification value data uint32");
+
+       free(list);
+
+       value1.type = INT64;
+       value1.store.d_int64 = 2;
+       value2.type = INT64;
+       value2.store.d_int64 = 3;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("name64");
+       key.type = INT64;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify int64");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != INT64,
+               "Failed to get correct notification value type int 64");
+       fail_if(list[0].store.d_int64 != 3,
+               "Failed to get correct notification value data int64");
+
+       free(list);
+
+       value1.type = UINT64;
+       value1.store.d_uint64 = 2;
+       value2.type = UINT64;
+       value2.store.d_uint64 = 3;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("nameu64");
+       key.type = UINT64;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify uint64");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != UINT64,
+               "Failed to get correct notification value type uint64");
+       fail_if(list[0].store.d_uint64 != 3,
+               "Failed to get correct notification value data uint64");
+
+       free(list);
+
+       value1.type = FLOAT;
+       value1.store.d_float = 3.1F;
+       value2.type = FLOAT;
+       value2.store.d_float = 3.14F;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("namef");
+       key.type = FLOAT;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify float");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != FLOAT,
+               "Failed to get correct notification value type float");
+       fail_if(list[0].store.d_float != 3.14F,
+               "Failed to get correct notification value data float");
+
+       free(list);
+
+       value1.type = DOUBLE;
+       value1.store.d_double = 3.141F;
+       value2.type = DOUBLE;
+       value2.store.d_double = 3.1415F;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("named");
+       key.type = DOUBLE;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify double");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != DOUBLE,
+               "Failed to get correct notification value type double");
+       fail_if(list[0].store.d_double != 3.1415F,
+               "Failed to get correct notification value data double");
+
+       free(list);
+
+       value1.type = BOOLEAN;
+       value1.store.d_boolean = false;
+       value2.type = BOOLEAN;
+       value2.store.d_int32 = true;
+       key.group = buxton_string_pack("group");
+       key.name = buxton_string_pack("nameb");
+       key.type = BOOLEAN;
+       r = buxton_direct_set_value(&daemon.buxton, &key,
+                                   &value1, NULL);
+       fail_if(!r, "Failed to set value for notify");
+       register_notification(&daemon, &cl, &key, 0, &status);
+       fail_if(status != 0,
+               "Failed to register notification for notify");
+       buxtond_notify_clients(&daemon, &cl, &key, &value2);
+
+       s = read(client, buf, 4096);
+       fail_if(s < 0, "Read from client failed");
+       csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list);
+       fail_if(csize != 1,
+               "Failed to get correct response to notify bool");
+       fail_if(msg != BUXTON_CONTROL_CHANGED,
+               "Failed to get correct control type");
+       fail_if(msgid != 0, "Failed to get correct message id");
+       fail_if(list[0].type != BOOLEAN,
+               "Failed to get correct notification value type bool");
+       fail_if(list[0].store.d_boolean != true,
+               "Failed to get correct notification value data bool");
+
+       free(list);
+       close(client);
+       buxton_direct_close(&daemon.buxton);
+}
+END_TEST
+
+START_TEST(identify_client_check)
+{
+       int sender;
+       client_list_item client;
+       bool r;
+       int32_t msg = 5;
+
+       setup_socket_pair(&client.fd, &sender);
+       r = identify_client(&client);
+       fail_if(r, "Identified client without message");
+
+       write(sender, &msg, sizeof(int32_t));
+       r = identify_client(&client);
+       fail_if(!r, "Identify client failed");
+
+       close(client.fd);
+       close(sender);
+}
+END_TEST
+
+START_TEST(add_pollfd_check)
+{
+       BuxtonDaemon daemon;
+       int fd;
+       short events;
+       bool a;
+
+       fd = 3;
+       daemon.nfds_alloc = 0;
+       daemon.accepting_alloc = 0;
+       daemon.nfds = 0;
+       daemon.pollfds = NULL;
+       daemon.accepting = NULL;
+       events = 1;
+       a = true;
+       add_pollfd(&daemon, fd, events, a);
+       fail_if(daemon.nfds != 1, "Failed to increase nfds");
+       fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd");
+       fail_if(daemon.pollfds[0].events != events, "Failed to set events");
+       fail_if(daemon.pollfds[0].revents != 0, "Failed to set revents");
+       fail_if(daemon.accepting[0] != a, "Failed to set accepting status");
+       free(daemon.pollfds);
+       free(daemon.accepting);
+}
+END_TEST
+
+START_TEST(del_pollfd_check)
+{
+       BuxtonDaemon daemon;
+       int fd;
+       short events;
+       bool a;
+
+       fd = 3;
+       daemon.nfds_alloc = 0;
+       daemon.accepting_alloc = 0;
+       daemon.nfds = 0;
+       daemon.pollfds = NULL;
+       daemon.accepting = NULL;
+       events = 1;
+       a = true;
+       add_pollfd(&daemon, fd, events, a);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd");
+       del_pollfd(&daemon, 0);
+       fail_if(daemon.nfds != 0, "Failed to decrease nfds 1");
+
+       fd = 4;
+       events = 2;
+       a = false;
+       add_pollfd(&daemon, fd, events, a);
+       fail_if(daemon.nfds != 1, "Failed to increase nfds after del");
+       fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd after del");
+       fail_if(daemon.pollfds[0].events != events,
+               "Failed to set events after del");
+       fail_if(daemon.pollfds[0].revents != 0,
+               "Failed to set revents after del");
+       fail_if(daemon.accepting[0] != a,
+               "Failed to set accepting status after del");
+       fd = 5;
+       events = 3;
+       a = true;
+       add_pollfd(&daemon, fd, events, a);
+       del_pollfd(&daemon, 0);
+       fail_if(daemon.nfds != 1, "Failed to delete fd 2");
+       fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd after del2");
+       fail_if(daemon.pollfds[0].events != events,
+               "Failed to set events after del2");
+       fail_if(daemon.pollfds[0].revents != 0,
+               "Failed to set revents after del2");
+       fail_if(daemon.accepting[0] != a,
+               "Failed to set accepting status after del2");
+}
+END_TEST
+
+START_TEST(handle_smack_label_check)
+{
+       client_list_item client;
+       int server;
+
+       setup_socket_pair(&client.fd, &server);
+       handle_smack_label(&client);
+
+       close(client.fd);
+       close(server);
+}
+END_TEST
+
+START_TEST(terminate_client_check)
+{
+       client_list_item *client;
+       BuxtonDaemon daemon;
+       int dummy;
+
+       client = malloc0(sizeof(client_list_item));
+       fail_if(!client, "client malloc failed");
+       client->smack_label = malloc0(sizeof(BuxtonString));
+       fail_if(!client->smack_label, "smack label malloc failed");
+       daemon.client_list = client;
+       setup_socket_pair(&client->fd, &dummy);
+       daemon.nfds_alloc = 0;
+       daemon.accepting_alloc = 0;
+       daemon.nfds = 0;
+       daemon.pollfds = NULL;
+       daemon.accepting = NULL;
+       add_pollfd(&daemon, client->fd, 2, false);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd");
+       client->smack_label->value = strdup("dummy");
+       client->smack_label->length = 6;
+       fail_if(!client->smack_label->value, "label strdup failed");
+
+       terminate_client(&daemon, client, 0);
+       fail_if(daemon.client_list, "Failed to set client list item to NULL");
+       close(dummy);
+}
+END_TEST
+
+START_TEST(handle_client_check)
+{
+       BuxtonDaemon daemon;
+       int dummy;
+       uint8_t buf[4096];
+       uint8_t *message = NULL;
+       BuxtonData data1, data2, data3, data4;
+       BuxtonArray *list = NULL;
+       bool r;
+       size_t ret;
+       uint32_t bsize;
+
+       list = buxton_array_new();
+       data1.type = STRING;
+       data1.store.d_string = buxton_string_pack("test-gdbm-user");
+       data2.type = STRING;
+       data2.store.d_string = buxton_string_pack("daemon-check");
+       data3.type = STRING;
+       data3.store.d_string = buxton_string_pack("name");
+       data4.type = UINT32;
+       data4.store.d_uint32 = STRING;
+       r = buxton_array_add(list, &data1);
+       fail_if(!r, "Failed to add data to array");
+       r = buxton_array_add(list, &data2);
+       fail_if(!r, "Failed to add data to array");
+       r = buxton_array_add(list, &data3);
+       fail_if(!r, "Failed to add data to array");
+       r = buxton_array_add(list, &data4);
+       fail_if(!r, "Failed to add data to array");
+       ret = buxton_serialize_message(&message, BUXTON_CONTROL_GET, 0, list);
+       fail_if(ret == 0, "Failed to serialize string data");
+       daemon.client_list = malloc0(sizeof(client_list_item));
+       fail_if(!daemon.client_list, "client malloc failed");
+       setup_socket_pair(&daemon.client_list->fd, &dummy);
+       fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK);
+       daemon.nfds_alloc = 0;
+       daemon.accepting_alloc = 0;
+       daemon.nfds = 0;
+       daemon.pollfds = NULL;
+       daemon.accepting = NULL;
+       add_pollfd(&daemon, daemon.client_list->fd, 2, false);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd 1");
+       fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 1");
+       fail_if(daemon.client_list, "Failed to terminate client with no data");
+       close(dummy);
+
+       daemon.client_list = malloc0(sizeof(client_list_item));
+       fail_if(!daemon.client_list, "client malloc failed");
+       setup_socket_pair(&daemon.client_list->fd, &dummy);
+       fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK);
+       add_pollfd(&daemon, daemon.client_list->fd, 2, false);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd 2");
+       write(dummy, buf, 1);
+       fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 2");
+       fail_if(!daemon.client_list, "Terminated client with insufficient data");
+       fail_if(daemon.client_list->data, "Didn't clean up left over client data 1");
+
+       bsize = 0;
+       memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t));
+       write(dummy, message, BUXTON_MESSAGE_HEADER_LENGTH);
+       fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 3");
+       fail_if(daemon.client_list, "Failed to terminate client with bad size 1");
+       close(dummy);
+
+       daemon.client_list = malloc0(sizeof(client_list_item));
+       fail_if(!daemon.client_list, "client malloc failed");
+       setup_socket_pair(&daemon.client_list->fd, &dummy);
+       fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK);
+       add_pollfd(&daemon, daemon.client_list->fd, 2, false);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd 3");
+       bsize = BUXTON_MESSAGE_MAX_LENGTH + 1;
+       memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t));
+       write(dummy, message, BUXTON_MESSAGE_HEADER_LENGTH);
+       fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 4");
+       fail_if(daemon.client_list, "Failed to terminate client with bad size 2");
+       close(dummy);
+
+       daemon.client_list = malloc0(sizeof(client_list_item));
+       fail_if(!daemon.client_list, "client malloc failed");
+       setup_socket_pair(&daemon.client_list->fd, &dummy);
+       fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK);
+       add_pollfd(&daemon, daemon.client_list->fd, 2, false);
+       fail_if(daemon.nfds != 1, "Failed to add pollfd 4");
+       bsize = (uint32_t)ret;
+       memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t));
+       write(dummy, message, ret);
+       fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 5");
+       fail_if(!daemon.client_list, "Terminated client with correct data length");
+
+       for (int i = 0; i < 33; i++) {
+               write(dummy, message, ret);
+       }
+       fail_if(!handle_client(&daemon, daemon.client_list, 0), "No more data available");
+       fail_if(!daemon.client_list, "Terminated client with correct data length");
+       terminate_client(&daemon, daemon.client_list, 0);
+       fail_if(daemon.client_list, "Failed to remove client 1");
+       close(dummy);
+
+       //FIXME add SIGPIPE handler
+       /* daemon.client_list = malloc0(sizeof(client_list_item)); */
+       /* fail_if(!daemon.client_list, "client malloc failed"); */
+       /* setup_socket_pair(&daemon.client_list->fd, &dummy); */
+       /* fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); */
+       /* add_pollfd(&daemon, daemon.client_list->fd, 2, false); */
+       /* fail_if(daemon.nfds != 1, "Failed to add pollfd 5"); */
+       /* write(dummy, message, ret); */
+       /* close(dummy); */
+       /* fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 6"); */
+       /* fail_if(daemon.client_list, "Failed to terminate client"); */
+}
+END_TEST
+
+START_TEST(buxtond_eat_garbage_check)
+{
+       daemon_pid = 0;
+       sigset_t sigset;
+       pid_t pid;
+
+       unlink(buxton_socket());
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+       pid = fork();
+       fail_if(pid < 0, "couldn't fork");
+       if (pid) {              /* parent*/
+               BuxtonClient c;
+               FuzzContext fuzz;
+               time_t start;
+               bool keep_going = true;
+               int fd;
+
+
+               srand(0);
+               bzero(&fuzz, sizeof(FuzzContext));
+
+               daemon_pid = pid;
+               usleep(250*1000);
+               check_did_not_crash(daemon_pid, &fuzz);
+
+
+               fail_if(time(&start) == -1, "call to time() failed");
+               do {
+                       pid_t client;
+                       ssize_t bytes;
+                       time_t now;
+
+                       fail_if(time(&now) == -1, "call to time() failed");
+                       if (now - start >= fuzz_time) {
+                               keep_going = false;
+                       }
+
+                       fuzz.size = (unsigned int)rand() % 4096;
+                       for (int i=0; i < fuzz.size; i++) {
+                               fuzz.buf[i] = (uint8_t)(rand() % 255);
+                       }
+                       if ((fuzz.size >= 6) && (rand() % 4096)) {
+                               uint16_t control = (uint16_t)((rand() % (BUXTON_CONTROL_MAX-1)) + 1);
+
+                               /* magic */
+                               fuzz.buf[0] = 0x06;
+                               fuzz.buf[1] = 0x72;
+
+                               /* valid message type */
+                               memcpy((void*)(fuzz.buf + 2), (void*)(&control), sizeof(uint16_t));
+
+                               /* valid size */
+                               memcpy((void *)(fuzz.buf + 4), (void *)(&fuzz.size), sizeof(uint32_t));
+                       }
+                       client = fork();
+                       fail_if(client == -1, "couldn't fork");
+                       if (client == 0) {
+                               fd = buxton_open(&c);
+                               fail_if(fd == -1,
+                                       "Open failed with daemon%s", dump_fuzz(&fuzz));
+
+
+                               bytes = write(fd, (void*)(fuzz.buf), fuzz.size);
+                               fail_if(bytes == -1, "write failed: %m%s", dump_fuzz(&fuzz));
+                               fail_unless(bytes == fuzz.size, "write was %d instead of %d", bytes, fuzz.size);
+
+                               buxton_close(c);
+                               usleep(1*1000);
+
+                               check_did_not_crash(daemon_pid, &fuzz);
+                               exit(0);
+                       } else {
+                               int status;
+                               pid_t wait = waitpid(client, &status, 0);
+                               fail_if(wait == -1, "waitpid failed");
+                               fail_unless(WIFEXITED(status), "client died");
+                               fuzz.iteration++;
+                       }
+               } while (keep_going);
+       } else {                /* child */
+               exec_daemon();
+       }
+}
+END_TEST
+
+BuxtonClient c;
+
+void cleanup(void)
+{
+       //used to cleanup the helper groups, values, names that are needed by other fuzzed values/labels
+
+       fail_if(buxton_open(&c) == -1, "Cleanup: Open failed with daemon.");
+
+       BuxtonKey key = buxton_key_create("tempgroup", NULL, "base", STRING);
+       fail_if(buxton_remove_group(c, key, NULL, "tempgroup", true), "Cleanup: Error at removing");
+       buxton_key_free(key);
+       buxton_close(c);
+
+}
+
+char *random_string(int str_size)
+{
+       //generates random strings of maximum str_size characters
+       //ignore this for now, it will be replaced by something more intelligent
+
+       int size;
+       char *str;
+
+       size = rand() % str_size;
+       str = calloc((size_t)(size + 1), sizeof(char));
+
+       for (int i = 0; i < size ; i++) {
+               str[i] = (char)(rand() % 255 + 1); //1-255, use % 25+97 to use lower-case alpha chars
+       }
+
+       return str;
+}
+
+static void SIGPIPE_handler(int signo)
+{
+       buxton_close(c);
+
+       //reconnect
+       fail_if(buxton_open(&c) == -1,"SIGPIPE: Open failed with daemon.");
+}
+
+START_TEST(buxtond_fuzz_commands)
+{
+/*     Previous fuzzer had correct MAGIC, CONTROL code, Message SIZE and randomized everything else.
+ *     This only randomizes the Data Value.
+ *
+ *     If you want to fuzz the protocol, use the above fuzzer.
+ *     If you want to fuzz the daemon, use this one. Use export BUXTON_FUZZER=NEW
+ */
+
+       daemon_pid = 0;
+       pid_t pid;
+       sigset_t sigset;
+       struct sigaction sa;
+       char *random_group, *random_layer, *random_label, *random_value, *random_name;
+       int max_length = 32768;
+       unlink(buxton_socket());
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+       // since the daemon will close the connection with the client if it receives a "weird" command
+       // we have to treat SIGPIPE
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIGPIPE_handler;
+       sigaction(SIGPIPE, &sa, NULL);
+
+       printf("============== CAUTION!!! Fuzzer at work =================\n");
+       FILE *f = fopen("debug_check_daemon.txt", "w");
+       fclose(f);
+
+       pid = fork();
+       fail_if(pid < 0, "couldn't fork");
+       if (pid) {              /* parent*/
+               FuzzContext fuzz;
+               time_t start;
+               bool keep_going = true;
+
+               srand((unsigned int) time(NULL));
+               bzero(&fuzz, sizeof(FuzzContext));
+
+               usleep(250*1000); //wait for daemon to start
+
+               fail_if(time(&start) == -1, "call to time() failed");
+               do {
+                       BuxtonKey key = NULL;
+                       time_t now;
+
+                       fail_if(time(&now) == -1, "call to time() failed");
+                       if (now - start >= fuzz_time) {
+                               keep_going = false;
+                       }
+
+                       cleanup();
+
+                       /* create a random group and layer */
+                       random_group = random_string(max_length);
+                       random_layer = random_string(max_length);
+
+                       key = buxton_key_create(random_group, NULL, random_layer, STRING);
+                       fail_if(!key, "Failed to create key");
+                       fail_if(buxton_open(&c) == -1, "Open failed with daemon.");
+
+                       f = fopen("debug_check_daemon.txt", "w");
+                       fail_if(!f, "Unable to open file\n");
+                       fprintf(f, "Create group: Group: %s\t Layer: %s\n", random_group, random_layer);
+                       fflush(f);
+
+                       if (buxton_create_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "1: Group created!\n");
+                       } else {
+                               fprintf(f, "1: Group was NOT created.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       /* create a random group in an existing layer */
+                       key = buxton_key_create(random_group, NULL, "base", STRING);
+                       fail_if(!key, "Failed to create key");
+                       fprintf(f, "Create group: Group: %s\t Layer: base\n", random_group);
+                       fflush(f);
+
+                       if (buxton_create_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "1: Group created!\n");
+                       } else {
+                               fprintf(f, "1: Group was NOT created.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       // create a random name on random group on a random layer
+                       random_name = random_string(max_length);
+                       key = buxton_key_create(random_group, random_name, random_layer, STRING);
+                       fail_if(!key, "Failed to create key");
+
+                       fprintf(f, "Create name: Group: %s\t Layer: %s\t Name:%s\n", random_group, random_layer, random_name);
+                       fflush(f);
+
+                       if (buxton_create_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "2: Name created!\n");
+                       } else {
+                               fprintf(f, "2: Name was NOT created.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       // create a random name on existing group
+                       // create group
+                       key = buxton_key_create("tempgroup", NULL, "base", STRING);
+                       fail_if(!key, "Failed to create key");
+                       fail_if(buxton_create_group(c, key, NULL,"tempgroup", true), "Creating group in buxton failed.");
+                       buxton_key_free(key);
+
+                       // put name on group
+                       key = buxton_key_create("tempgroup", random_name, "base", STRING);
+                       fail_if(!key, "Failed to create key");
+                       fprintf(f, "Create name: Group: tempgroup\t Layer: base\t Name: %s\n", random_name);
+
+                       if (buxton_create_group(c, key, NULL, "tempgroup", true)) {
+                               fprintf(f, "2: Name created!\n");
+                       } else {
+                               fprintf(f, "2: Name was NOT created.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       // create a random value on a existing labeled group
+                       //create the "existing" group, plus the name
+                       random_value = random_string(max_length);
+                       BuxtonKey group = buxton_key_create("tempgroup", NULL, "base", STRING);
+                       fail_if(!group, "Failed to create key for group");
+                       key = buxton_key_create("tempgroup", "name", "base", STRING);
+                       fail_if(!key, "Failed to create key");
+
+                       // set the a correct label and randomized value
+                       fprintf(f, "Set label: Group: tgroup\t Layer: base\t Value: %s\n", random_value);
+                       fflush(f);
+                       fail_if(buxton_set_label(c, group, "*", NULL, NULL, true), "Setting label in buxton failed.");
+
+                       if (buxton_set_value(c, key, random_value, NULL, "tgroup", true)) {
+                               fprintf(f, "3: Value was set!\n");
+                       } else {
+                               fprintf(f, "3: Value was NOT set.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(group);
+                       buxton_key_free(key);
+
+                       //set a random label on an existing group
+                       random_label = random_string(3);
+                       group = buxton_key_create("tempgroup", NULL, "base", STRING);
+                       fail_if(!group, "Failed to create key for group");
+
+                       fprintf(f, "Set label: Group: tempgroup\t Layer: base\t Label: %s\n", random_label);
+                       if (buxton_set_label(c, group, random_label, NULL, group, true)) {
+                               fprintf(f, "3: Label was set!\n");
+                       } else {
+                               fprintf(f, "3: Label was NOT set.\n");
+                       }
+                       fflush(f);
+
+                       //set random value/label on name
+                       BuxtonKey name = buxton_key_create("tempgroup", "name", "base", STRING);
+                       fail_if(!name, "Failed to create key for name");
+
+                       fprintf(f, "Set label and value: Group: tempgroup\t Layer: base\t Name: name\t Value: %s\t Label: %s \n", random_value, random_label);
+                       if (buxton_set_value(c, name, random_value, NULL, NULL, true)) {
+                               fprintf(f, "4: Value on name was set!\n");
+                       } else {
+                               fprintf(f, "4: Value on name  was NOT set.\n");
+                       }
+                       free(random_value);
+                       fflush(f);
+
+                       if (buxton_set_label(c, name, random_label, NULL, name, true)) {
+                               fprintf(f, "4: Label on name was set!\n");
+                       } else {
+                               fprintf(f, "4: Label on name was NOT set.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(group);
+                       buxton_key_free(name);
+                       free(random_label);
+
+                       // remove name from group
+                       key = buxton_key_create(random_group, random_name, random_layer, STRING);
+                       fprintf(f, "Remove group: Group: %s\t Layer: %s\t Name:%s\n", random_group, random_layer, random_name);
+                       if (buxton_remove_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "5: Name from group was removed!\n");
+                       } else {
+                               fprintf(f, "5: Name from group was NOT removed.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       // remove name from existing group
+                       key = buxton_key_create("tempgroup", random_name, "base", STRING);
+                       fprintf(f, "Remove group: Group: tempgroup\t Layer: base\t Name:%s\n", random_name);
+                       if (buxton_remove_group(c, key, NULL, "tempgroup", true)) {
+                               fprintf(f, "5: Name from group was removed!\n");
+                       } else {
+                               fprintf(f, "5: Name from group was NOT removed.\n");
+                       }
+                       fflush(f);
+                       free(random_name);
+                       buxton_key_free(key);
+
+                       /* remove group from existing layer*/
+                       key = buxton_key_create(random_group, NULL, "base", STRING);
+                       fail_if(!key, "Failed to create key");
+                       fprintf(f, "Remove group: Group: %s\t Layer: base\n", random_group);
+                       if (buxton_remove_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "5: Group was removed!\n");
+                       } else {
+                               fprintf(f, "5: Group was NOT removed.\n");
+                       }
+                       fflush(f);
+                       buxton_key_free(key);
+
+                       /* remove group */
+                       key = buxton_key_create(random_group, NULL, random_layer, STRING);
+                       fail_if(!key, "Failed to create key");
+                       fprintf(f, "Remove group: Group: %s\t Layer: %s\n", random_group, random_layer);
+                       if (buxton_remove_group(c, key, NULL, random_group, true)) {
+                               fprintf(f, "5: Group was removed!\n");
+                       } else {
+                               fprintf(f, "5: Group was NOT removed.\n");
+                       }
+                       buxton_key_free(key);
+                       fflush(f);
+
+                       buxton_close(c);
+                       usleep(1*1000);
+
+                       fprintf(f, "5: Closed comm.\n");
+                       fclose(f);
+                       free(random_layer);
+                       free(random_group);
+
+                       reap_callbacks();
+               } while (keep_going);
+       } else {                /* child */
+               exec_daemon();
+       }
+
+       usleep(3 * 1000);
+       reap_callbacks();
+}
+END_TEST
+
+static Suite *
+daemon_suite(void)
+{
+       Suite *s;
+       TCase *tc;
+       char *fuzzer_engine;
+
+       s = suite_create("daemon");
+       tc = tcase_create("daemon test functions");
+       tcase_add_checked_fixture(tc, setup, teardown);
+       tcase_add_test(tc, buxton_open_check);
+       tcase_add_test(tc, buxton_create_group_check);
+       tcase_add_test(tc, buxton_remove_group_check);
+       tcase_add_test(tc, buxton_set_value_check);
+       tcase_add_test(tc, buxton_set_label_check);
+       tcase_add_test(tc, buxton_get_value_for_layer_check);
+       tcase_add_test(tc, buxton_get_value_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("buxton_daemon_functions");
+       tcase_add_test(tc, parse_list_check);
+       tcase_add_test(tc, create_group_check);
+       tcase_add_test(tc, remove_group_check);
+       tcase_add_test(tc, set_label_check);
+
+       tcase_add_test(tc, set_value_check);
+       tcase_add_test(tc, get_value_check);
+       tcase_add_test(tc, register_notification_check);
+       tcase_add_test(tc, buxtond_handle_message_error_check);
+       tcase_add_test(tc, buxtond_handle_message_create_group_check);
+       tcase_add_test(tc, buxtond_handle_message_remove_group_check);
+       tcase_add_test(tc, buxtond_handle_message_set_label_check);
+       tcase_add_test(tc, buxtond_handle_message_set_value_check);
+       tcase_add_test(tc, buxtond_handle_message_get_check);
+       tcase_add_test(tc, buxtond_handle_message_notify_check);
+       tcase_add_test(tc, buxtond_handle_message_unset_check);
+       tcase_add_test(tc, buxtond_notify_clients_check);
+       tcase_add_test(tc, identify_client_check);
+       tcase_add_test(tc, add_pollfd_check);
+       tcase_add_test(tc, del_pollfd_check);
+       tcase_add_test(tc, handle_smack_label_check);
+       tcase_add_test(tc, terminate_client_check);
+       tcase_add_test(tc, handle_client_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("buxton daemon evil tests");
+       tcase_add_checked_fixture(tc, NULL, teardown);
+       fuzzer_engine = getenv("BUXTON_FUZZER");
+       if (fuzzer_engine) {
+               if (strcmp(fuzzer_engine,"NEW") == 0) {
+                       tcase_add_test(tc, buxtond_fuzz_commands);
+               } else {
+                       tcase_add_test(tc, buxtond_eat_garbage_check);
+               }
+       } else {
+               tcase_add_test(tc, buxtond_eat_garbage_check);
+       }
+       tcase_set_timeout(tc, fuzz_time+2);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
+
+int main(void)
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+       char *fuzzenv;
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf");
+       putenv("BUXTON_ROOT_CHECK=0");
+       fuzzenv = getenv("BUXTON_FUZZ_TIME");
+       if (fuzzenv) {
+               fuzz_time = atoi(fuzzenv);
+       } else {
+               fuzz_time = 2;
+       }
+       s = daemon_suite();
+       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;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_shared_lib.c b/test/check_shared_lib.c
new file mode 100644 (file)
index 0000000..cb257c1
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <check.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "backend.h"
+#include "buxtonlist.h"
+#include "check_utils.h"
+#include "hashmap.h"
+#include "log.h"
+#include "serialize.h"
+#include "smack.h"
+#include "util.h"
+#include "configurator.h"
+
+#ifdef NDEBUG
+#error "re-run configure with --enable-debug"
+#endif
+
+START_TEST(log_write_check)
+{
+       char log_file[] = "log-check-stderr-file";
+       char log_msg[] = "Log test";
+       _cleanup_free_ char *log_read = malloc(strlen(log_msg));
+       fail_if(log_read == NULL,
+               "Failed to allocate space for reading the log");
+
+       int old_stderr = fileno(stderr);
+       fail_if(old_stderr == -1, "Failed to get fileno for stderr");
+
+       int dup_stderr = dup(old_stderr);
+       fail_if(dup_stderr == -1, "Failed to dup stderr");
+
+       FILE *new_stderr = freopen(log_file, "w", stderr);
+       fail_if(new_stderr == NULL, "Failed to reopen stderr");
+
+       buxton_log(log_msg);
+       fail_if(fflush(stderr) != 0, "Failed to flush stderr");
+
+       FILE *read_test = fopen(log_file, "r");
+       fail_if(read_test == NULL, "Failed to open stderr file for reading");
+
+       size_t len = fread(log_read, 1, strlen(log_msg), read_test);
+       fail_if(len != strlen(log_msg), "Failed to read entire log message");
+       fail_if(strncmp(log_msg, log_read, strlen(log_msg)) != 0,
+               "Failed to write log message correctly");
+
+       fclose(stderr);
+       fclose(read_test);
+       stderr = fdopen(dup_stderr, "w");
+}
+END_TEST
+
+
+START_TEST(hashmap_check)
+{
+       Hashmap *map;
+       char *value;
+       int r;
+
+       map = hashmap_new(string_hash_func, string_compare_func);
+       fail_if(map == NULL, "Failed to allocated hashmap");
+       r = hashmap_put(map, "test", "passed");
+       fail_if(r < 0, "Failed to add element to hashmap");
+
+       value = hashmap_get(map, "test");
+
+       fail_if(value == NULL,
+               "Failed to get value from hashmap");
+
+       fail_if(strcmp(value, "passed") != 0,
+               "Failed to retrieve the put value");
+
+       hashmap_remove(map, "test");
+       fail_if(hashmap_isempty(map) != true,
+               "Failed to remove item from hashmap");
+
+       hashmap_free(map);
+}
+END_TEST
+
+static inline void array_free_fun(void *p)
+{
+       free(p);
+}
+
+START_TEST(array_check)
+{
+       BuxtonArray *array = NULL;
+       char *value;
+       char *element;
+       void *f;
+       bool r;
+
+       array = buxton_array_new();
+       fail_if(array == NULL, "Failed to allocate memory for BuxtonArray");
+       element = strdup("test");
+       fail_if(!element, "Failed to allocate memory for array item");
+       r = buxton_array_add(NULL, element);
+       fail_if(r, "Added element to NULL array");
+       r = buxton_array_add(array, NULL);
+       fail_if(r, "Added NULL element to array");
+       r = buxton_array_add(array, element);
+       fail_if(r  == false, "Failed to add element to BuxtonArray");
+       fail_if(array->len != 1,
+               "Failed to get correct value for number of elements in array");
+
+       f = buxton_array_get(NULL, 0);
+       fail_if(f, "Got value from NULL array");
+       f = buxton_array_get(array, (uint16_t)(array->len + 1));
+       fail_if(f, "Got value from index bigger than maximum index");
+       value = (char *)buxton_array_get(array, 0);
+
+       fail_if(value == NULL,
+               "Failed to get value from BuxtonArray");
+
+       fail_if(strcmp(value, "test") != 0,
+               "Failed to retrieve the stored value");
+
+       buxton_array_free(&array, array_free_fun);
+       fail_if(array != NULL,
+               "Failed to free BuxtonArray");
+}
+END_TEST
+
+START_TEST(list_check)
+{
+       BuxtonList *list = NULL;
+       int i;
+       char *tmp = NULL;
+       char *head = "<head of the list>";
+       char *head2 = "<prepend should appear before head now>";
+       char *data = "<middle element to be removed>";
+
+       /* Append a million strings. Results in about 3 million allocs
+        * due to asprintf, calloc of node, etc */
+       int DEFAULT_SIZE = (10*1000)*100;
+       for (i = 0; i <= DEFAULT_SIZE; i++) {
+               if (i == 5) {
+                       fail_if(buxton_list_append(&list, data) == false,
+                               "Failed to append to BuxtonList");
+               } else {
+                       asprintf(&tmp, "i #%d", i);
+                       fail_if(buxton_list_prepend(&list, tmp) == false,
+                               "Failed to prepend to BuxtonList");
+               }
+       }
+
+       fail_if(list->size != DEFAULT_SIZE, "List size invalid");
+
+       /* Prepend head */
+       fail_if(buxton_list_prepend(&list, head) != true, "Prepend head failed");
+       fail_if(list->size != DEFAULT_SIZE+1, "Prepended head size invalid");
+
+       /* Prepend head2 */
+       fail_if(buxton_list_prepend(&list, head2) != true, "Prepend head2 failed");
+       fail_if(list->size != DEFAULT_SIZE+2, "Prepended head2 size invalid");
+
+       /* Remove from middle */
+       fail_if(buxton_list_remove(&list, data, false) != true,
+               "List removal from middle failed");
+       fail_if(list->size != DEFAULT_SIZE+1, "List middle removal size invalid");
+
+       /* Remove from end */
+       fail_if(buxton_list_remove(&list, tmp, true) != true,
+               "List tail removal failed");
+       fail_if(list->size != DEFAULT_SIZE, "List tail removal size invalid");
+
+       fail_if(buxton_list_append(&list, "newend") != true,
+               "List new tail append failed");
+       fail_if(list->size != DEFAULT_SIZE+1, "List new tail size invalid");
+       fail_if(buxton_list_remove(&list, "newend", false) != true,
+               "List new tail removal failed");
+       fail_if(list->size != DEFAULT_SIZE,
+               "List new tail size invalid (post removal)");
+
+       /* Fake remove */
+       fail_if(buxton_list_remove(&list, "nonexistent", false) == true,
+               "List non existent removal should fail");
+       fail_if(list->size != DEFAULT_SIZE,
+               "List size invalid after no change");
+
+       /* Remove head */
+       fail_if(buxton_list_remove(&list, head, false) == false,
+               "List remove head failed");
+       fail_if(buxton_list_remove(&list, head2, false) == false,
+               "List remove head2 failed");
+       fail_if(list->size != DEFAULT_SIZE-2,
+               "List post heads removal size invalid");
+
+       buxton_list_free_all(&list);
+}
+END_TEST
+
+START_TEST(get_layer_path_check)
+{
+       BuxtonLayer layer;
+       char *path = NULL;
+       char *real_path = NULL;
+       int r;
+
+       memzero(&layer, sizeof(BuxtonLayer));
+       layer.name = buxton_string_pack("path-test");
+       layer.type = LAYER_SYSTEM;
+       r = asprintf(&real_path, "%s/%s", buxton_db_path(), "path-test.db");
+       fail_if(r == -1, "Failed to set real path for system layer");
+
+       path = get_layer_path(&layer);
+       fail_if(path == NULL, "Failed to get path for system layer");
+       fail_if(strcmp(path, real_path) != 0,
+               "Failed to set correct system path");
+
+       free(path);
+       free(real_path);
+
+       layer.name = buxton_string_pack("user-path-test");
+       layer.type = LAYER_USER;
+       layer.uid = 1000;
+       r = asprintf(&real_path, "%s/%s", buxton_db_path(), "user-path-test-1000.db");
+       fail_if(r == -1, "Failed to set real path for user layer");
+
+       path = get_layer_path(&layer);
+       fail_if(path == NULL, "Failed to get path for user layer");
+       fail_if(strcmp(path, real_path) != 0,
+               "Failed to set correct user path");
+
+       free(path);
+       free(real_path);
+
+       layer.name = buxton_string_pack("bad-type-test");
+       layer.type = -1;
+       fail_if(get_layer_path(&layer) != NULL,
+               "Invalid layer type didn't return failure");
+}
+END_TEST
+
+START_TEST(buxton_data_copy_check)
+{
+       BuxtonData original, copy;
+
+       original.type = STRING;
+       original.store.d_string = buxton_string_pack("test-data-copy");
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy string type");
+       fail_if(!copy.store.d_string.value, "Failed to copy string data");
+       fail_if((strcmp(original.store.d_string.value, copy.store.d_string.value) != 0),
+               "Incorrectly copied string data");
+       if (copy.store.d_string.value)
+               free(copy.store.d_string.value);
+
+       original.type = INT32;
+       original.store.d_int32 = INT_MAX;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy int32 type");
+       fail_if(original.store.d_int32 != copy.store.d_int32,
+               "Failed to copy int32 data");
+
+       original.type = UINT32;
+       original.store.d_uint32 = UINT_MAX;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy uint32 type");
+       fail_if(original.store.d_uint32 != copy.store.d_uint32,
+               "Failed to copy int32 data");
+
+       original.type = INT64;
+       original.store.d_int64 = LLONG_MAX;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy int64 type");
+       fail_if(original.store.d_int64 != copy.store.d_int64,
+               "Failed to copy int64 data");
+
+       original.type = UINT64;
+       original.store.d_uint64 = ULLONG_MAX;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy uint64 type");
+       fail_if(original.store.d_uint64 != copy.store.d_uint64,
+               "Failed to copy uint64 data");
+
+       original.type = FLOAT;
+       original.store.d_float = 3.14F;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy float type");
+       fail_if(original.store.d_float != copy.store.d_float,
+               "Failed to copy float data");
+
+       original.type = DOUBLE;
+       original.store.d_double = 3.1415;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy double type");
+       fail_if(original.store.d_double != copy.store.d_double,
+               "Failed to copy double data");
+
+       original.type = BOOLEAN;
+       original.store.d_boolean = true;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type != original.type,
+               "Failed to copy boolean type");
+       fail_if(original.store.d_boolean != copy.store.d_boolean,
+               "Failed to copy boolean data");
+
+       original.type = -1;
+       buxton_data_copy(&original, &copy);
+       fail_if(copy.type || copy.store.d_string.value, "Copied invalid data");
+}
+END_TEST
+
+START_TEST(buxton_type_as_string_check)
+{
+       BuxtonDataType type;
+
+       type = STRING;
+       fail_if(strcmp(buxton_type_as_string(type), "string") != 0,
+               "Failed to get string of STRING type");
+
+       type = INT32;
+       fail_if(strcmp(buxton_type_as_string(type), "int32_t") != 0,
+               "Failed to get string of INT32 type");
+
+       type = UINT32;
+       fail_if(strcmp(buxton_type_as_string(type), "uint32_t") != 0,
+               "Failed to get string of UINT32 type");
+
+       type = INT64;
+       fail_if(strcmp(buxton_type_as_string(type), "int64_t") != 0,
+               "Failed to get string of INT64 type");
+
+       type = UINT64;
+       fail_if(strcmp(buxton_type_as_string(type), "uint64_t") != 0,
+               "Failed to get string of UINT64 type");
+
+       type = FLOAT;
+       fail_if(strcmp(buxton_type_as_string(type), "float") != 0,
+               "Failed to get string of FLOAT type");
+
+       type = DOUBLE;
+       fail_if(strcmp(buxton_type_as_string(type), "double") != 0,
+               "Failed to get string of DOUBLE type");
+
+       type = BOOLEAN;
+       fail_if(strcmp(buxton_type_as_string(type), "boolean") != 0,
+               "Failed to get string of BOOLEAN type");
+}
+END_TEST
+
+START_TEST(_write_check)
+{
+       int in, out;
+       uint8_t buf[10];
+
+       setup_socket_pair(&in, &out);
+       fail_if(fcntl(in, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+       fail_if(fcntl(out, F_SETFL, O_NONBLOCK),
+               "Failed to set socket to non blocking");
+
+       buf[0] = 1;
+       fail_if(!_write(out, buf, 1), "Failed to write 1 byte");
+}
+END_TEST
+
+START_TEST(buxton_db_serialize_check)
+{
+       BuxtonData dsource, dtarget;
+       uint8_t *packed = NULL;
+       BuxtonString lsource, ltarget;
+
+       dsource.type = STRING;
+       lsource = buxton_string_pack("label");
+       dsource.store.d_string = buxton_string_pack("test-string");
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize string data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for string");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination string labels differ");
+       fail_if(strcmp(dsource.store.d_string.value, dtarget.store.d_string.value) != 0,
+               "Source and destination string data differ");
+       free(packed);
+       free(ltarget.value);
+       if (dtarget.store.d_string.value)
+               free(dtarget.store.d_string.value);
+
+       dsource.type = INT32;
+       dsource.store.d_int32 = INT_MAX;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize int32 data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for int32");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination int32 labels differ");
+       fail_if(dsource.store.d_int32 != dtarget.store.d_int32,
+               "Source and destination int32 data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = UINT32;
+       dsource.store.d_uint32 = UINT_MAX;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize uint32 data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for uint32");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination uint32 labels differ");
+       fail_if(dsource.store.d_uint32 != dtarget.store.d_uint32,
+               "Source and destination uint32 data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = INT64;
+       dsource.store.d_int64 = LONG_MAX;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize int64 data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for int64");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination int64 labels differ");
+       fail_if(dsource.store.d_int64 != dtarget.store.d_int64,
+               "Source and destination int64 data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = UINT64;
+       dsource.store.d_uint64 = ULLONG_MAX;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize uint64 data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for uint64");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination uint64 labels differ");
+       fail_if(dsource.store.d_uint64 != dtarget.store.d_uint64,
+               "Source and destination uint64 data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = FLOAT;
+       dsource.store.d_float = 3.14F;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize float data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for float");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination float labels differ");
+       fail_if(dsource.store.d_float != dtarget.store.d_float,
+               "Source and destination float data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = DOUBLE;
+       dsource.store.d_double = 3.1415;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize double data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for double");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination double labels differ");
+       fail_if(dsource.store.d_double != dtarget.store.d_double,
+               "Source and destination double data differ");
+       free(ltarget.value);
+       free(packed);
+
+       dsource.type = BOOLEAN;
+       dsource.store.d_boolean = true;
+       fail_if(buxton_serialize(&dsource, &lsource, &packed) == false,
+               "Failed to serialize boolean data");
+       buxton_deserialize(packed, &dtarget, &ltarget);
+       fail_if(dsource.type != dtarget.type,
+               "Source and destination type differ for boolean");
+       fail_if(strcmp(lsource.value, ltarget.value) != 0,
+               "Source and destination boolean labels differ");
+       free(ltarget.value);
+       free(packed);
+}
+END_TEST
+
+START_TEST(buxton_message_serialize_check)
+{
+       BuxtonControlMessage csource;
+       BuxtonControlMessage ctarget;
+       BuxtonData dsource1, dsource2;
+       uint16_t control, message;
+       BuxtonData *dtarget = NULL;
+       uint8_t *packed = NULL;
+       BuxtonArray *list = NULL;
+       BuxtonArray *list2 = NULL;
+       size_t ret;
+       size_t pcount;
+       bool r;
+       uint32_t msource;
+       uint32_t mtarget;
+
+       list = buxton_array_new();
+       fail_if(!list, "Failed to allocate list");
+       dsource1.type = STRING;
+       dsource1.store.d_string = buxton_string_pack("test-key");
+       csource = BUXTON_CONTROL_GET;
+       msource = 0;
+       r = buxton_array_add(list, &dsource1);
+       fail_if(!r, "Failed to add element to array");
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize string data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize string data");
+       fail_if(ctarget != csource, "Failed to get correct control message for string");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for string");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for string");
+       fail_if(strcmp(dsource1.store.d_string.value, dtarget[0].store.d_string.value) != 0,
+               "Source and destination string data differ");
+       free(packed);
+       if (dtarget) {
+               if (dtarget[0].store.d_string.value) {
+                       free(dtarget[0].store.d_string.value);
+               }
+               free(dtarget);
+       }
+
+       dsource1.type = INT32;
+       dsource1.store.d_int32 = INT_MAX;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize int32 data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize int data");
+       fail_if(ctarget != csource, "Failed to get correct control message for int32");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for int32");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for int32");
+       fail_if(dsource1.store.d_int32 != dtarget[0].store.d_int32,
+               "Source and destination int32 data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = UINT32;
+       dsource1.store.d_uint32 = UINT_MAX;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize uint32 data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize uint32 data");
+       fail_if(ctarget != csource, "Failed to get correct control message for uint32");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for uint32");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for uint32");
+       fail_if(dsource1.store.d_uint32 != dtarget[0].store.d_uint32,
+               "Source and destination uint32 data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = INT64;
+       dsource1.store.d_int64 = LONG_MAX;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize long data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize long data");
+       fail_if(ctarget != csource, "Failed to get correct control message for long");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for long");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for long");
+       fail_if(dsource1.store.d_int64 != dtarget[0].store.d_int64,
+               "Source and destination long data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = UINT64;
+       dsource1.store.d_uint64 = ULLONG_MAX;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize uint64 data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize uint64 data");
+       fail_if(ctarget != csource, "Failed to get correct control message for uint64");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for uint64");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for uint64");
+       fail_if(dsource1.store.d_uint64 != dtarget[0].store.d_uint64,
+               "Source and destination uint64 data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = FLOAT;
+       dsource1.store.d_float = 3.14F;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize float data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize float data");
+       fail_if(ctarget != csource, "Failed to get correct control message for float");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for float");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for float");
+       fail_if(dsource1.store.d_float != dtarget[0].store.d_float,
+               "Source and destination float data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = DOUBLE;
+       dsource1.store.d_double = 3.1415;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize double data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize double data");
+       fail_if(ctarget != csource, "Failed to get correct control message for double");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for double");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for double");
+       fail_if(dsource1.store.d_double != dtarget[0].store.d_double,
+               "Source and destination double data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = BOOLEAN;
+       dsource1.store.d_boolean = true;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize boolean data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 1,
+               "Failed to deserialize boolean data");
+       fail_if(ctarget != csource, "Failed to get correct control message for boolean");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for boolean");
+       fail_if(dsource1.type != dtarget[0].type,
+               "Source and destination type differ for boolean");
+       fail_if(dsource1.store.d_boolean != dtarget[0].store.d_boolean,
+               "Source and destination boolean data differ");
+       free(packed);
+       free(dtarget);
+
+       dsource1.type = INT32;
+       dsource1.store.d_int32 = 1;
+       dsource2.type = INT32;
+       dsource2.store.d_int32 = 2;
+       csource = BUXTON_CONTROL_STATUS;
+       r = buxton_array_add(list, &dsource2);
+       fail_if(!r, "Failed to add element to array");
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret == 0, "Failed to serialize 2arg data");
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) != 2,
+               "Failed to deserialize 2arg data");
+       fail_if(ctarget != csource, "Failed to get correct control message for 2arg");
+       fail_if(mtarget != msource,
+               "Failed to get correct message id for 2arg");
+       fail_if(dsource1.type != dtarget[0].type,
+               "1 Source and destination type differ for 2arg");
+       fail_if(dsource1.store.d_int32 != dtarget[0].store.d_int32,
+               "1 Source and destination differ for 2arg data");
+       fail_if(dsource2.type != dtarget[1].type,
+               "2 Source and destination type differ for 2arg");
+       fail_if(dsource2.store.d_int32 != dtarget[1].store.d_int32,
+               "2 Source and destination differ for 2arg data");
+       free(packed);
+       free(dtarget);
+
+       list2 = buxton_array_new();
+       fail_if(!list, "Failed to allocate list");
+       list2->len = 0;
+       dsource1.type = STRING;
+       dsource1.store.d_string = buxton_string_pack("test-key");
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list2);
+       fail_if(ret == 0, "Unable to serialize with 0 element list");
+
+       list2->len = BUXTON_MESSAGE_MAX_PARAMS + 1;
+       ret = buxton_serialize_message(&packed, csource, msource, list2);
+       fail_if(ret != 0, "Serialized with too many parameters");
+
+       list2->len = 0;
+       r = buxton_array_add(list2, &dsource1);
+       fail_if(!r, "Failed to add element to array");
+       list2->len = 2;
+       ret = buxton_serialize_message(&packed, csource, msource, list2);
+       fail_if(ret != 0, "Serialized with incorrect parameter count");
+       list2->len = 0;
+
+       dsource1.type = -1;
+       dsource1.store.d_string = buxton_string_pack("test-key");
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret != 0, "Serialized with bad data type");
+
+       dsource1.type = STRING;
+       dsource1.store.d_string = buxton_string_pack("test-key");
+       csource = -1;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(ret != 0, "Serialized with bad message type");
+
+       dsource1.type = INT32;
+       dsource1.store.d_int32 = INT_MAX;
+       csource = BUXTON_CONTROL_GET;
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       fail_if(buxton_deserialize_message(packed, &ctarget,
+                                          BUXTON_MESSAGE_HEADER_LENGTH - 1,
+                                          &mtarget, &dtarget) >= 0,
+               "Deserialized message with too small a length data");
+
+       /* don't read past end of buffer check */
+       fail_if(buxton_deserialize_message(packed, &ctarget,
+                                          (sizeof(uint32_t) * 3)
+                                          + sizeof(uint32_t)
+                                          + sizeof(uint16_t)
+                                          + (sizeof(uint32_t) * 2),
+                                          &mtarget, &dtarget) >= 0,
+               "Deserialized message size smaller than minimum data length");
+
+       control = 0x0000;
+       memcpy(packed, &control, sizeof(uint16_t));
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) >= 0,
+               "Deserialized message with invalid control");
+       free(packed);
+
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       message = BUXTON_CONTROL_MIN;
+       memcpy(packed+sizeof(uint16_t), &message, sizeof(uint16_t));
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) >= 0,
+               "Deserialized message with invalid control");
+       free(packed);
+
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       message = BUXTON_CONTROL_MAX;
+       memcpy(packed+sizeof(uint16_t), &message, sizeof(uint16_t));
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) >= 0,
+               "Deserialized message with invalid control");
+       free(packed);
+
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       pcount = 0;
+       memcpy(packed+(2 * sizeof(uint32_t)+sizeof(uint32_t)), &pcount, sizeof(uint32_t));
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) < 0,
+               "Unable to deserialize message with 0 BuxtonData");
+       free(packed);
+
+       ret = buxton_serialize_message(&packed, csource, msource, list);
+       pcount = BUXTON_MESSAGE_MAX_PARAMS + 1;
+       memcpy(packed+(2 * sizeof(uint32_t)+sizeof(uint32_t)), &pcount, sizeof(uint32_t));
+       fail_if(buxton_deserialize_message(packed, &ctarget, ret, &mtarget,
+                                          &dtarget) >= 0,
+               "Unable to deserialize message with 0 BuxtonData");
+       free(packed);
+
+       buxton_array_free(&list, NULL);
+       buxton_array_free(&list2, NULL);
+}
+END_TEST
+
+START_TEST(buxton_get_message_size_check)
+{
+       BuxtonControlMessage csource;
+       BuxtonData dsource;
+       uint8_t *packed = NULL;
+       BuxtonArray *list = NULL;
+       size_t ret;
+       bool r;
+
+       list = buxton_array_new();
+       fail_if(!list, "Failed to allocate list");
+       dsource.type = STRING;
+       dsource.store.d_string = buxton_string_pack("test-key");
+       csource = BUXTON_CONTROL_GET;
+       r = buxton_array_add(list, &dsource);
+       fail_if(!r, "Failed to add element to array");
+       ret = buxton_serialize_message(&packed, csource, 0, list);
+       fail_if(ret == 0, "Failed to serialize string data for size");
+       fail_if(ret != buxton_get_message_size(packed, ret),
+               "Failed to get correct message size");
+       fail_if(buxton_get_message_size(packed, BUXTON_MESSAGE_HEADER_LENGTH - 1) != 0,
+               "Got size even though message smaller than the minimum");
+
+       free(packed);
+       buxton_array_free(&list, NULL);
+}
+END_TEST
+
+static Suite *
+shared_lib_suite(void)
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("shared_lib");
+       tc = tcase_create("log_functions");
+       tcase_add_test(tc, log_write_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("hashmap_functions");
+       tcase_add_test(tc, hashmap_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("array_functions");
+       tcase_add_test(tc, array_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("list_functions");
+       tcase_add_test(tc, list_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("util_functions");
+       tcase_add_test(tc, get_layer_path_check);
+       tcase_add_test(tc, buxton_data_copy_check);
+       tcase_add_test(tc, buxton_type_as_string_check);
+       tcase_add_test(tc, _write_check);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("buxton_serialize_functions");
+       tcase_add_test(tc, buxton_db_serialize_check);
+       tcase_add_test(tc, buxton_message_serialize_check);
+       tcase_add_test(tc, buxton_get_message_size_check);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
+
+int main(void)
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf");
+       s = shared_lib_suite();
+       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;
+}
+
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_smack.c b/test/check_smack.c
new file mode 100644 (file)
index 0000000..107c4f1
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+#include <check.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "buxton.h"
+#include "configurator.h"
+#include "check_utils.h"
+#include "daemon.h"
+#include "log.h"
+#include "smack.h"
+#include "util.h"
+
+#ifdef NDEBUG
+       #error "re-run configure with --enable-debug"
+#endif
+
+static pid_t daemon_pid;
+
+static void exec_daemon(void)
+{
+       char path[PATH_MAX];
+
+       //FIXME: path is wrong for makedistcheck
+       snprintf(path, PATH_MAX, "%s/check_buxtond", get_current_dir_name());
+
+       if (execl(path, "check_buxtond", (const char*)NULL) < 0) {
+               fail("couldn't exec: %m");
+       }
+       fail("should never reach here");
+}
+
+static void setup(void)
+{
+       daemon_pid = 0;
+       sigset_t sigset;
+       pid_t pid;
+
+       unlink(buxton_socket());
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+       pid = fork();
+       fail_if(pid < 0, "couldn't fork");
+       if (pid) {
+               /* parent*/
+               daemon_pid = pid;
+               usleep(128*1000);
+       } else {
+               /* child */
+               exec_daemon();
+       }
+}
+
+static void teardown(void)
+{
+       if (daemon_pid) {
+               int status;
+               pid_t pid;
+
+               pid = waitpid(daemon_pid, &status, WNOHANG);
+               fail_if(pid == -1, "waitpid error");
+               if (pid) {
+                       fail("daemon crashed!");
+               } else {
+                       /* if the daemon is still running, kill it */
+                       kill(SIGTERM, daemon_pid);
+                       usleep(64*1000);
+                       kill(SIGKILL, daemon_pid);
+               }
+       }
+}
+
+START_TEST(smack_access_check)
+{
+       bool ret;
+       BuxtonString subject;
+       BuxtonString object;
+
+       ret = buxton_cache_smack_rules();
+       fail_if(!ret, "Failed to cache Smack rules");
+
+       subject = buxton_string_pack("system");
+       object = buxton_string_pack("base/sample/key");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access was denied, but should have been granted");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(ret, "Write access was granted, but should have been denied");
+
+       subject = buxton_string_pack("system");
+       object = buxton_string_pack("system/sample/key");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access was denied");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(!ret, "Write access was denied");
+
+       subject = buxton_string_pack("*");
+       object = buxton_string_pack("foo");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(ret, "Read access granted for * subject");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(ret, "Write access granted for * subject");
+
+       subject = buxton_string_pack("foo");
+       object = buxton_string_pack("@");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for @ object");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(!ret, "Write access denied for @ object");
+
+       subject = buxton_string_pack("@");
+       object = buxton_string_pack("foo");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for @ subject");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(!ret, "Write access denied for @ subject");
+
+       subject = buxton_string_pack("foo");
+       object = buxton_string_pack("*");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for * object");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(!ret, "Write access denied for * object");
+
+       subject = buxton_string_pack("foo");
+       object = buxton_string_pack("foo");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for matching subject/object");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(!ret, "Write access denied for matching subject/object");
+
+       subject = buxton_string_pack("foo");
+       object = buxton_string_pack("_");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for _ object");
+
+       subject = buxton_string_pack("^");
+       object = buxton_string_pack("foo");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(!ret, "Read access denied for ^ subject");
+
+       subject = buxton_string_pack("subjecttest");
+       object = buxton_string_pack("objecttest");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_READ);
+       fail_if(ret, "Read access granted for unrecognized subject/object");
+
+       subject = buxton_string_pack("subjecttest");
+       object = buxton_string_pack("objecttest");
+       ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE);
+       fail_if(ret, "Write access granted for unrecognized subject/object");
+}
+END_TEST
+
+static Suite *
+daemon_suite(void)
+{
+       Suite *s;
+       TCase *tc;
+       bool __attribute__((unused))dummy;
+
+       s = suite_create("smack");
+
+       dummy = buxton_cache_smack_rules();
+       if (buxton_smack_enabled()) {
+               tc = tcase_create("smack access test functions");
+               tcase_add_checked_fixture(tc, setup, teardown);
+               /* TODO: add tests that use actual client Smack labels */
+               suite_add_tcase(s, tc);
+
+               tc = tcase_create("smack libsecurity functions");
+               tcase_add_test(tc, smack_access_check);
+               suite_add_tcase(s, tc);
+       } else {
+               buxton_log("Smack support not detected; skipping this test suite\n");
+       }
+
+       return s;
+}
+
+int main(void)
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+
+       putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf");
+       s = daemon_suite();
+       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;
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_utils.c b/test/check_utils.c
new file mode 100644 (file)
index 0000000..fda288a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+#include <check.h>
+#include <sys/socket.h>
+
+#include "check_utils.h"
+
+void setup_socket_pair(int *client, int *server)
+{
+       int socks[2];
+       fail_if(socketpair(AF_UNIX, SOCK_STREAM, 0, socks),
+               "socketpair: %m");
+
+       *client = socks[0];
+       *server = socks[1];
+}
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/check_utils.h b/test/check_utils.h
new file mode 100644 (file)
index 0000000..81e1ae9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * This file is part of buxton.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * buxton is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ */
+
+/**
+ * \file check_utils.h Internal header
+ * This file is used internally by buxton to provide functionality
+ * used for testing
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+       #include "config.h"
+#endif
+
+/**
+ * Set up a socket pair
+ * @param client Client socket file descriptor
+ * @param server Server socket file descriptor
+ */
+void setup_socket_pair(int *client, int *server);
+
+/*
+ * Editor modelines  - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/test/test-configurator.conf b/test/test-configurator.conf
new file mode 100644 (file)
index 0000000..cd693d2
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Test config for buxton configurator
+#
+
+[Configuration]
+ModuleDirectory=/shut/your/mouth
+DatabasePath=/you/are/so/suck
+SmackLoadFile=/smack/smack/smack
+SocketPath=/hurp/durp/durp
+
+[base]
+Type=System
+Backend=gdbm
+Description=Operating System configuration layer
+Priority=0
+# This will end up being a file at @@DB_PATH@@/base.db
+
+[isp]
+Type=System
+Backend=gdbm
+Description=ISP specific settings
+Priority=1
+# This will end up being a file at @@DB_PATH@@/isp.db
+
+[temp]
+Type=System
+Backend=memory
+Priority=99
+Description=A termporary layer for scratch settings and data
+# This will not end up in any file
+
+[user]
+Type=User
+Backend=gdbm
+Priority=1000
+Description=Per-user settings
+# This will end up in @@DB_PATH@@/user-<uid>.db
+
+[test-gdbm]
+Type=System
+Backend=gdbm
+Priority=5000
+Description="GDBM test db"
+
+[test-memory]
+Type=System
+Backend=memory
+Priority=5001
+Description="Memory test db"
+
+[test-gdbm-user]
+Type=User
+Backend=gdbm
+Priority=6000
+Description=GDBM test db for user
diff --git a/test/test-fail.ini.in b/test/test-fail.ini.in
new file mode 100644 (file)
index 0000000..d194594
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# All of these should trigger syntax errors
+#
+[default]
+fail
+boat
+fail \
+boat
+50 - 8 ;
+x + y ;
diff --git a/test/test-pass.ini.in b/test/test-pass.ini.in
new file mode 100644 (file)
index 0000000..3c364dc
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# This is an example of a layer config file
+#
+
+[Defaults]
+Layers = OS, Platform, System, Custom, User;
+SmackLabel= "_";
+Backend = gdbm;
+
+[OS]
+Description  = "Operating System configuration layer";
+
+[Platform]
+Description  = "Platform configuration layer";
+Backend = memory;
+
+[System]
+Description  = "System configuration layer";
+
+[Custom]
+Description  = "Special configuration layer";
+SmackLabel= "CarrierLocked";
+
+[User]
+Description  = "Per User configuration layer";
+
diff --git a/test/test.conf.in b/test/test.conf.in
new file mode 100644 (file)
index 0000000..ac26e35
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Default buxton config file
+#
+
+[Configuration]
+ModuleDirectory=@abs_top_builddir@/.libs
+DatabasePath=@abs_top_builddir@/test/databases
+SmackLoadFile=@abs_top_srcdir@/test/test.load2
+SocketPath=@abs_top_builddir@/test/buxton-socket
+
+[base]
+Type=System
+Backend=gdbm
+Description=Operating System configuration layer
+Priority=0
+# This will end up being a file at @@DB_PATH@@/base.db
+
+[isp]
+Type=System
+Backend=gdbm
+Description=ISP specific settings
+Priority=1
+# This will end up being a file at @@DB_PATH@@/isp.db
+
+[temp]
+Type=System
+Backend=memory
+Priority=99
+Description=A termporary layer for scratch settings and data
+# This will not end up in any file
+
+[user]
+Type=User
+Backend=gdbm
+Priority=1000
+Description=Per-user settings
+# This will end up in @@DB_PATH@@/user-<uid>.db
+
+[test-gdbm]
+Type=System
+Backend=gdbm
+Priority=5000
+Description="GDBM test db"
+
+[test-memory]
+Type=System
+Backend=memory
+Priority=5001
+Description="Memory test db"
+
+[test-gdbm-user]
+Type=User
+Backend=gdbm
+Priority=6000
+Description=GDBM test db for user
diff --git a/test/test.load2 b/test/test.load2
new file mode 100644 (file)
index 0000000..afea080
--- /dev/null
@@ -0,0 +1,13 @@
+_ base/sample/key rw
+_ system/sample/key rw
+_ user/sample/key r
+system base/sample/key r
+system system/sample/key rw
+system user/sample/key r
+user base/sample/key r
+user system/sample/key r
+user user/sample/key rw
+_ System rw
+_ User rw
+System _ rw
+User _ rw