--- /dev/null
+/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
--- /dev/null
+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>
--- /dev/null
+# 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
--- /dev/null
+ 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!
--- /dev/null
+# 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
--- /dev/null
+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.
--- /dev/null
+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:
--- /dev/null
+#!/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
--- /dev/null
+#!/bin/sh
+
+rm -f test/databases/*.db
--- /dev/null
+
+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}
+])
--- /dev/null
+#
+# 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
--- /dev/null
+[Unit]
+Description=Buxton Configuration Service
+
+[Service]
+ExecStart=@prefix@/sbin/buxtond
+User=@BUXTON_USERNAME@
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+[Unit]
+Description=Buxton Configuration Service
+
+[Socket]
+ListenStream=@BUXTON_SOCKET@
+
+[Install]
+WantedBy=sockets.target
--- /dev/null
+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}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+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.
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+.so buxton_open.3
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+.so buxton_key_create.3
--- /dev/null
+.so buxton_key_create.3
--- /dev/null
+.so buxton_key_create.3
--- /dev/null
+.so buxton_key_create.3
--- /dev/null
+.so buxton_key_create.3
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+.so buxton_create_group.3
--- /dev/null
+.so buxton_response_status.3
--- /dev/null
+'\" 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
--- /dev/null
+.so buxton_response_status.3
--- /dev/null
+.so buxton_response_status.3
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+.so buxton_register_notification.3
--- /dev/null
+.so buxton_set_value.3
--- /dev/null
+'\" 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
--- /dev/null
+'\" 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
--- /dev/null
+<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>
--- /dev/null
+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
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+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:
+ *;
+};
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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);
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*-*- 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;
+}
--- /dev/null
+/*-*- 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))
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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);
--- /dev/null
+/*-*- 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))
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*-*- 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)))
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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, ©->group)) {
+ goto fail;
+ }
+ }
+ if (original->name.value) {
+ if (!buxton_string_copy(&original->name, ©->name)) {
+ goto fail;
+ }
+ }
+ if (original->layer.value) {
+ if (!buxton_string_copy(&original->layer, ©->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:
+ */
--- /dev/null
+/*-*- 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, ©);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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, <arget);
+ 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# All of these should trigger syntax errors
+#
+[default]
+fail
+boat
+fail \
+boat
+50 - 8 ;
+x + y ;
--- /dev/null
+#
+# 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";
+
--- /dev/null
+#
+# 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
--- /dev/null
+_ 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