From b9084026eeccc618e6321c47ccedb48aeceb3872 Mon Sep 17 00:00:00 2001 From: "jk7744.park" Date: Sat, 31 Jan 2015 16:11:56 +0900 Subject: [PATCH] tizen 2.3 release --- .gitignore | 56 + AUTHORS | 7 + Doxyfile | 1869 ++++++++++++++++++++++ LICENSE.LGPL2.1 | 502 ++++++ Makefile.am | 468 ++++++ README | 39 + TODO | 93 ++ autogen.sh | 14 + check_db_clean | 3 + configure.ac | 243 +++ data/buxton.conf | 45 + data/buxton.service.in | 9 + data/buxton.socket.in | 8 + data/libbuxton.pc.in | 11 + demo/gtk_client.c | 348 +++++ demo/gtk_client.h | 49 + demo/hellocreategroup.c | 99 ++ demo/helloget.c | 107 ++ demo/hellonotify.c | 136 ++ demo/helloremovegroup.c | 99 ++ demo/helloset.c | 110 ++ demo/hellosetlabel.c | 108 ++ demo/hellounset.c | 99 ++ demo/notifytest.c | 175 +++ demo/timing.c | 312 ++++ docs/LICENSE.MIT | 19 + docs/buxton.7 | 69 + docs/buxton.conf.5 | 126 ++ docs/buxton_client_handle_response.3 | 63 + docs/buxton_close.3 | 1 + docs/buxton_create_group.3 | 235 +++ docs/buxton_get_value.3 | 158 ++ docs/buxton_key_create.3 | 109 ++ docs/buxton_key_free.3 | 1 + docs/buxton_key_get_group.3 | 1 + docs/buxton_key_get_layer.3 | 1 + docs/buxton_key_get_name.3 | 1 + docs/buxton_key_get_type.3 | 1 + docs/buxton_open.3 | 95 ++ docs/buxton_register_notification.3 | 199 +++ docs/buxton_remove_group.3 | 1 + docs/buxton_response_key.3 | 1 + docs/buxton_response_status.3 | 94 ++ docs/buxton_response_type.3 | 1 + docs/buxton_response_value.3 | 1 + docs/buxton_set_conf_file.3 | 92 ++ docs/buxton_set_label.3 | 159 ++ docs/buxton_set_value.3 | 246 +++ docs/buxton_unregister_notification.3 | 1 + docs/buxton_unset_value.3 | 1 + docs/buxtonctl.1 | 228 +++ docs/buxtond.8 | 95 ++ m4/.empty | 0 packaging/buxton.manifest | 11 + packaging/buxton.spec | 82 + src/cli/client.c | 586 +++++++ src/cli/client.h | 203 +++ src/cli/main.c | 357 +++++ src/core/daemon.c | 1134 ++++++++++++++ src/core/daemon.h | 266 ++++ src/core/main.c | 408 +++++ src/db/gdbm.c | 452 ++++++ src/db/memory.c | 347 +++++ src/include/buxton.h | 380 +++++ src/libbuxton/lbuxton.c | 740 +++++++++ src/libbuxton/lbuxton.sym | 27 + src/security/smack.c | 260 ++++ src/security/smack.h | 95 ++ src/shared/backend.c | 272 ++++ src/shared/backend.h | 171 ++ src/shared/buxtonarray.c | 101 ++ src/shared/buxtonarray.h | 85 + src/shared/buxtonclient.h | 46 + src/shared/buxtondata.h | 66 + src/shared/buxtonkey.h | 42 + src/shared/buxtonlist.c | 150 ++ src/shared/buxtonlist.h | 119 ++ src/shared/buxtonresponse.h | 41 + src/shared/buxtonstring.h | 37 + src/shared/configurator.c | 346 +++++ src/shared/configurator.h | 134 ++ src/shared/dictionary.c | 425 +++++ src/shared/dictionary.h | 188 +++ src/shared/direct.c | 719 +++++++++ src/shared/direct.h | 169 ++ src/shared/hashmap.c | 928 +++++++++++ src/shared/hashmap.h | 110 ++ src/shared/iniparser.c | 779 ++++++++++ src/shared/iniparser.h | 330 ++++ src/shared/list.h | 136 ++ src/shared/log.c | 39 + src/shared/log.h | 39 + src/shared/macro.h | 83 + src/shared/protocol.c | 937 +++++++++++ src/shared/protocol.h | 244 +++ src/shared/serialize.c | 570 +++++++ src/shared/serialize.h | 133 ++ src/shared/util.c | 353 +++++ src/shared/util.h | 238 +++ test/check_buxton.c | 1229 +++++++++++++++ test/check_configurator.c | 346 +++++ test/check_daemon.c | 2759 +++++++++++++++++++++++++++++++++ test/check_shared_lib.c | 894 +++++++++++ test/check_smack.c | 228 +++ test/check_utils.c | 38 + test/check_utils.h | 42 + test/test-configurator.conf | 55 + test/test-fail.ini.in | 10 + test/test-pass.ini.in | 26 + test/test.conf.in | 55 + test/test.load2 | 13 + 111 files changed, 26081 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 Doxyfile create mode 100644 LICENSE.LGPL2.1 create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100755 autogen.sh create mode 100755 check_db_clean create mode 100644 configure.ac create mode 100644 data/buxton.conf create mode 100644 data/buxton.service.in create mode 100644 data/buxton.socket.in create mode 100644 data/libbuxton.pc.in create mode 100644 demo/gtk_client.c create mode 100644 demo/gtk_client.h create mode 100644 demo/hellocreategroup.c create mode 100644 demo/helloget.c create mode 100644 demo/hellonotify.c create mode 100644 demo/helloremovegroup.c create mode 100644 demo/helloset.c create mode 100644 demo/hellosetlabel.c create mode 100644 demo/hellounset.c create mode 100644 demo/notifytest.c create mode 100644 demo/timing.c create mode 100644 docs/LICENSE.MIT create mode 100644 docs/buxton.7 create mode 100644 docs/buxton.conf.5 create mode 100644 docs/buxton_client_handle_response.3 create mode 100644 docs/buxton_close.3 create mode 100644 docs/buxton_create_group.3 create mode 100644 docs/buxton_get_value.3 create mode 100644 docs/buxton_key_create.3 create mode 100644 docs/buxton_key_free.3 create mode 100644 docs/buxton_key_get_group.3 create mode 100644 docs/buxton_key_get_layer.3 create mode 100644 docs/buxton_key_get_name.3 create mode 100644 docs/buxton_key_get_type.3 create mode 100644 docs/buxton_open.3 create mode 100644 docs/buxton_register_notification.3 create mode 100644 docs/buxton_remove_group.3 create mode 100644 docs/buxton_response_key.3 create mode 100644 docs/buxton_response_status.3 create mode 100644 docs/buxton_response_type.3 create mode 100644 docs/buxton_response_value.3 create mode 100644 docs/buxton_set_conf_file.3 create mode 100644 docs/buxton_set_label.3 create mode 100644 docs/buxton_set_value.3 create mode 100644 docs/buxton_unregister_notification.3 create mode 100644 docs/buxton_unset_value.3 create mode 100644 docs/buxtonctl.1 create mode 100644 docs/buxtond.8 create mode 100644 m4/.empty create mode 100644 packaging/buxton.manifest create mode 100644 packaging/buxton.spec create mode 100644 src/cli/client.c create mode 100644 src/cli/client.h create mode 100644 src/cli/main.c create mode 100644 src/core/daemon.c create mode 100644 src/core/daemon.h create mode 100644 src/core/main.c create mode 100644 src/db/gdbm.c create mode 100644 src/db/memory.c create mode 100644 src/include/buxton.h create mode 100644 src/libbuxton/lbuxton.c create mode 100644 src/libbuxton/lbuxton.sym create mode 100644 src/security/smack.c create mode 100644 src/security/smack.h create mode 100644 src/shared/backend.c create mode 100644 src/shared/backend.h create mode 100644 src/shared/buxtonarray.c create mode 100644 src/shared/buxtonarray.h create mode 100644 src/shared/buxtonclient.h create mode 100644 src/shared/buxtondata.h create mode 100644 src/shared/buxtonkey.h create mode 100644 src/shared/buxtonlist.c create mode 100644 src/shared/buxtonlist.h create mode 100644 src/shared/buxtonresponse.h create mode 100644 src/shared/buxtonstring.h create mode 100644 src/shared/configurator.c create mode 100644 src/shared/configurator.h create mode 100644 src/shared/dictionary.c create mode 100644 src/shared/dictionary.h create mode 100644 src/shared/direct.c create mode 100644 src/shared/direct.h create mode 100644 src/shared/hashmap.c create mode 100644 src/shared/hashmap.h create mode 100644 src/shared/iniparser.c create mode 100644 src/shared/iniparser.h create mode 100644 src/shared/list.h create mode 100644 src/shared/log.c create mode 100644 src/shared/log.h create mode 100644 src/shared/macro.h create mode 100644 src/shared/protocol.c create mode 100644 src/shared/protocol.h create mode 100644 src/shared/serialize.c create mode 100644 src/shared/serialize.h create mode 100644 src/shared/util.c create mode 100644 src/shared/util.h create mode 100644 test/check_buxton.c create mode 100644 test/check_configurator.c create mode 100644 test/check_daemon.c create mode 100644 test/check_shared_lib.c create mode 100644 test/check_smack.c create mode 100644 test/check_utils.c create mode 100644 test/check_utils.h create mode 100644 test/test-configurator.conf create mode 100644 test/test-fail.ini.in create mode 100644 test/test-pass.ini.in create mode 100644 test/test.conf.in create mode 100644 test/test.load2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f009d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +/Makefile +*.log +/*.tar.bz2 +/*.tar.gz +/*.tar.xz +*.la +*.lo +*.o +*~ +Makefile.in +aclocal.m4 +config.* +configure +autom4te.cache +compile +install-sh +missing +ltmain.sh +depcomp +stamp-* +libtool +test-driver +.deps +.libs +m4/*.m4 +.dirstamp +*.trs +/cscope.* +/tags +buxtond +buxtonctl +check_buxton +data/buxton.service +data/buxton.socket +data/*.pc +check_shared_lib +log-check-stderr-file +test/*.ini +src/shared/constants.c +check_daemon +check_buxtond +check_smack +bxt_timing +bxt_gtk_client +*.gcda +*.gcno +/coverage +test/databases/*.db +test/test.conf +check_configurator +GPATH +GRTAGS +GTAGS +bxt_hello* +buxton-* +debug_check_daemon.txt diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..35243ec --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +Auke Kok +Brad Peters +Michael Ikey Doherty +Michael Leibowitz +Patrick McCarty +Shane Bryan +William Douglas diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..d2ed4d1 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1869 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "Buxton" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Configuration system" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = "docs" + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and 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 , where +# is the value of the INPUT_FILTER tag, and 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 +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/LICENSE.LGPL2.1 b/LICENSE.LGPL2.1 new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/LICENSE.LGPL2.1 @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3d2ec41 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,468 @@ +# 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 + +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) + +buxtonctl_SOURCES = \ + src/cli/main.c \ + src/cli/client.c \ + src/cli/client.h + +buxtonctl_CFLAGS = \ + $(AM_CFLAGS) + +buxtonctl_LDADD = \ + libbuxton.la \ + libbuxton-shared.la + +noinst_LTLIBRARIES += \ + libbuxton-shared.la + +libbuxton_shared_la_SOURCES = \ + src/security/smack.c \ + src/security/smack.h \ + src/shared/backend.c \ + src/shared/backend.h \ + src/shared/buxtonarray.c \ + src/shared/buxtonarray.h \ + src/shared/buxtonclient.h \ + src/shared/buxtondata.h \ + src/shared/buxtonkey.h \ + src/shared/buxtonlist.c \ + src/shared/buxtonlist.h \ + src/shared/buxtonresponse.h \ + src/shared/buxtonstring.h \ + src/shared/configurator.c \ + src/shared/configurator.h \ + src/shared/direct.c \ + src/shared/direct.h \ + src/shared/hashmap.c \ + src/shared/hashmap.h \ + src/shared/list.h \ + src/shared/log.c \ + src/shared/log.h \ + src/shared/macro.h \ + src/shared/protocol.c \ + src/shared/protocol.h \ + src/shared/serialize.c \ + src/shared/serialize.h \ + src/shared/util.c \ + src/shared/util.h \ + ${NULL} + +if USE_LOCAL_INIPARSER +libbuxton_shared_la_SOURCES += \ + src/shared/dictionary.c \ + src/shared/dictionary.h \ + src/shared/iniparser.c \ + src/shared/iniparser.h +endif + +libbuxton_shared_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -static + +include_HEADERS += \ + src/include/buxton.h + +lib_LTLIBRARIES += \ + libbuxton.la + +libbuxton_la_SOURCES = \ + src/libbuxton/lbuxton.c + +libbuxton_la_CFLAGS = \ + $(AM_CFLAGS) \ + @INIPARSER_CFLAGS@ \ + -fvisibility=hidden + +libbuxton_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + @INIPARSER_LIBS@ \ + -version-info $(LIBBUXTON_CURRENT):$(LIBBUXTON_REVISION):$(LIBBUXTON_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libbuxton/lbuxton.sym + +libbuxton_la_LIBADD = \ + libbuxton-shared.la \ + -ldl + +pkglib_LTLIBRARIES += \ + gdbm.la \ + memory.la + +gdbm_la_SOURCES = \ + src/db/gdbm.c + +gdbm_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -fvisibility=hidden \ + -module \ + -avoid-version + +gdbm_la_LIBADD = \ + -lgdbm + +memory_la_SOURCES = \ + src/db/memory.c + +memory_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -fvisibility=hidden \ + -module \ + -avoid-version + +check_PROGRAMS = \ + check_buxton \ + check_shared_lib \ + check_buxtond \ + check_daemon \ + check_smack \ + check_configurator + +check_buxton_SOURCES = \ + test/check_utils.c \ + test/check_utils.h \ + test/check_buxton.c +check_buxton_CFLAGS = \ + $(AM_CFLAGS) \ + @CHECK_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_buxton_LDADD = \ + @CHECK_LIBS@ \ + libbuxton.la \ + libbuxton-shared.la + +check_shared_lib_SOURCES = \ + test/check_utils.c \ + test/check_utils.h \ + test/check_shared_lib.c +check_shared_lib_CFLAGS = \ + $(AM_CFLAGS) \ + @CHECK_CFLAGS@ \ + @INIPARSER_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_shared_lib_LDADD = \ + @CHECK_LIBS@ \ + @INIPARSER_LIBS@ \ + libbuxton.la \ + libbuxton-shared.la + +check_buxtond_SOURCES = \ + test/check_utils.c \ + test/check_utils.h \ + src/core/daemon.c \ + src/core/daemon.h \ + src/core/main.c +check_buxtond_CFLAGS = \ + @CHECK_CFLAGS@ \ + $(AM_CFLAGS) \ + @INIPARSER_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_buxtond_LDADD = \ + @CHECK_LIBS@ \ + $(SYSTEMD_LIBS) \ + libbuxton.la \ + libbuxton-shared.la + +check_daemon_SOURCES = \ + test/check_utils.c \ + test/check_utils.h \ + src/core/daemon.c \ + src/core/daemon.h \ + test/check_daemon.c +check_daemon_CFLAGS = \ + $(AM_CFLAGS) \ + @CHECK_CFLAGS@ \ + @INIPARSER_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_daemon_LDADD = \ + @CHECK_LIBS@ \ + @INIPARSER_LIBS@ \ + libbuxton.la \ + libbuxton-shared.la + +check_smack_SOURCES = \ + test/check_utils.c \ + test/check_utils.h \ + test/check_smack.c +check_smack_CFLAGS = \ + $(AM_CFLAGS) \ + @CHECK_CFLAGS@ \ + @INIPARSER_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_smack_LDADD = \ + @CHECK_LIBS@ \ + @INIPARSER_LIBS@ \ + libbuxton.la \ + libbuxton-shared.la + +check_configurator_SOURCES = \ + src/shared/configurator.c \ + src/shared/configurator.h \ + test/check_configurator.c +check_configurator_CFLAGS = \ + $(AM_CFLAGS) \ + @CHECK_CFLAGS@ \ + @INIPARSER_CFLAGS@ \ + -DMAKE_CHECK \ + -DABS_TOP_SRCDIR=\"$(abs_top_srcdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +check_configurator_LDADD = \ + @CHECK_LIBS@ \ + @INIPARSER_LIBS@ \ + libbuxton-shared.la + +check_DATA = \ + test/test-pass.ini \ + test/test-fail.ini \ + test/test.conf \ + test/test.load2 \ + ${NULL} + +if BUILD_DEMOS +bin_PROGRAMS += \ + bxt_timing \ + bxt_hello_get \ + bxt_hello_set \ + bxt_hello_set_label \ + bxt_hello_create_group \ + bxt_hello_remove_group \ + bxt_hello_unset \ + bxt_hello_notify \ + bxt_hello_notify_multi + +# Timing test +bxt_timing_SOURCES = \ + demo/timing.c +bxt_timing_LDADD = \ + libbuxton.la \ + libbuxton-shared.la \ + -lrt -lm + +bxt_hello_get_SOURCES = \ + demo/helloget.c +bxt_hello_get_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_get_LDADD = \ + libbuxton.la + +bxt_hello_set_SOURCES = \ + demo/helloset.c +bxt_hello_set_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_set_LDADD = \ + libbuxton.la + +bxt_hello_set_label_SOURCES = \ + demo/hellosetlabel.c +bxt_hello_set_label_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_set_label_LDADD = \ + libbuxton.la + +bxt_hello_create_group_SOURCES = \ + demo/hellocreategroup.c +bxt_hello_create_group_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_create_group_LDADD = \ + libbuxton.la + +bxt_hello_remove_group_SOURCES = \ + demo/helloremovegroup.c +bxt_hello_remove_group_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_remove_group_LDADD = \ + libbuxton.la + +bxt_hello_unset_SOURCES = \ + demo/hellounset.c +bxt_hello_unset_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_unset_LDADD = \ + libbuxton.la + +bxt_hello_notify_SOURCES = \ + demo/hellonotify.c +bxt_hello_notify_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_notify_LDADD = \ + libbuxton.la + +bxt_hello_notify_multi_SOURCES = \ + demo/notifytest.c +bxt_hello_notify_multi_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_notify_multi_LDADD = \ + libbuxton.la + +if BUILD_GTK_DEMO +bin_PROGRAMS += \ + bxt_gtk_client +# GTK3 client demo +bxt_gtk_client_SOURCES = \ + demo/gtk_client.c \ + demo/gtk_client.h +bxt_gtk_client_LDADD = \ + $(GTK3_LIBS) \ + libbuxton.la \ + libbuxton-shared.la +bxt_gtk_client_CFLAGS = \ + $(GTK3_CFLAGS) \ + $(AM_CFLAGS) +endif + +endif diff --git a/README b/README new file mode 100644 index 0000000..dd7dd2b --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +Buxton +====== + +Buxton is a security-enabled configuration management system. It features a +layered approach to configuration storage, with each layer containing an +arbitrary number of groups, each of which may contain key-value pairs. +Mandatory Access Control (MAC) is implemented at the group level and at the +key-value level. + +Buxton provides a C library (libbuxton) for client applications to use. +Internally, buxton uses a daemon (buxtond) for processing client requests and +enforcing MAC. Also, a CLI (buxtonctl) is provided for interactive use and for +use in shell scripts. + + +Build dependencies +================== + +- attr, to provide a required header file, xattr.h + +- check, to build the unit tests + +- gdbm, for key-value pair storage + +- Linux kernel headers, for the inotify header + +- systemd, for autodetection of service file locations, for socket activation + of buxtond, and the initialization of the Smack virtual filesystem + + +Additional runtime dependencies +=============================== + +- Smack-enabled kernel, for MAC support + * CONFIG_SECURITY_SMACK=y + + +Note: if running a kernel without Smack enabled, buxton can still be used for +configuration management, but MAC will not be enforced. diff --git a/TODO b/TODO new file mode 100644 index 0000000..d3559e9 --- /dev/null +++ b/TODO @@ -0,0 +1,93 @@ +TODO format: +Description: (description of task) +Difficulty: [Simple|Medium|Complex] +Time to complete: (estimate in days) +Target: Version to be released in +Status: If multi part change, what has been done so far + +Description: Update logic for smack access checks +Difficulty: Medium +Time to complete: 6 +Target: v1 +Status: In progress. Refer to https://github.com/sofar/buxton/wiki/Smack-TODO + for details. + +Description: Test multi-part messaging in the client-side of the library, + mirroring changes made to buxtond's protocol handling +Difficulty: Complex +Time to complete: ?? +Target: ?? +Status: + +Description: add checks for data consistency, and determine if gdbm needs to be + "reorganised" to ensure minimal I/O bottlenecking +Difficulty: Medium +Time to complete: 4 +Target: ?? +Status: + +Description: Tests for nonblocking calls between client and server +Difficulty: Simple +Time to complete: 4 +Target: ?? +Status: + +Description: Fixup list keys +Difficulty: Medium +Time to complete: 5 +Target: ?? +Status: + +Description: Bulk send receive messages +Difficulty: Complex +Time to complete: ?? +Target: ?? +Status: + +Description: Complete code coverage (minus exceptional cases) +Difficulty: Simple +Time to complete: 10 +Target: after v1 +Status: 79.6% of lines covered + +Description: Use BuxtonArray for message deserialization +Difficulty: Medium +Time to complete: 5 +Target: v2 +Status: + +Description: Remove iniparser from buxton +Difficulty: Simple +Time to complete: Done +Target: ?? +Status: Needs merged but Ubuntu doesn't include it yet so holding off for now + +Description: Tizen programs converted to use Buxton +Difficulty: Complex +Time to complete: 10 +Target: v1 +Status: + +Description: Allow notification registration for non existant keys +Difficulty: Simple +Time to complete: 3 +Target: ?? +Status: + +Description: Add client library support for list_keys +Difficulty: Medium +Time to complete: 3 +Target: ?? +Status: + +Description: Ensure all public apis give copies of data +Difficulty: Simple +Time to complete: 1 +Target: ?? +Status: All done aside from list_keys which needs to be updated for the new api + +Description: Add Wextra to compiler flags once iniparser is gone +Difficulty: Simple +Time to complete: 1 +Target: ?? +Status: diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..ebc0ee1 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +autoreconf --force --install --symlink --warnings=all + +args="\ +--sysconfdir=/etc \ +--localstatedir=/var \ +--prefix=/usr \ +--enable-silent-rules" + +./configure CFLAGS='-g -O0' $args "$@" +make clean diff --git a/check_db_clean b/check_db_clean new file mode 100755 index 0000000..a814976 --- /dev/null +++ b/check_db_clean @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -f test/databases/*.db diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..decec58 --- /dev/null +++ b/configure.ac @@ -0,0 +1,243 @@ + +AC_PREREQ([2.68]) +AC_INIT([buxton],[2],[william.douglas@intel.com],[buxton],[https://github.com/sofar/buxton]) +AM_INIT_AUTOMAKE([foreign -Wall -Werror -Wno-portability silent-rules subdir-objects color-tests no-dist-gzip dist-xz]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_SRCDIR([src/core/main.c]) +AC_CONFIG_HEADERS([config.h]) +AC_PREFIX_DEFAULT(/usr/local) + +LT_PREREQ(2.2) +LT_INIT([disable-static]) + +# Checks for programs. +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_INSTALL +AC_PROG_MKDIR_P + +# Checks for libraries. +AC_CHECK_LIB([dl], [dlopen]) +AC_CHECK_LIB([gdbm], [gdbm_open]) +AC_CHECK_LIB([rt], [clock_gettime]) +AC_CHECK_LIB([m], [sqrt]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UID_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_FUNC_STRTOD +AC_CHECK_FUNCS([atexit]) +AC_CHECK_FUNCS([memmove]) +AC_CHECK_FUNCS([memset]) +AC_CHECK_FUNCS([socket]) +AC_CHECK_FUNCS([strchr]) +AC_CHECK_FUNCS([strdup]) +AC_CHECK_FUNCS([strndup]) +AC_CHECK_FUNCS([strtol]) +AC_CHECK_FUNCS([__secure_getenv secure_getenv]) + +AC_MSG_CHECKING([[whether preprocessor supports #pragma once]]) +AC_PREPROC_IFELSE( + [AC_LANG_PROGRAM([[#pragma once]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_PRAGMA_ONCE], [1], [Preprocessor support for #pragma once]) + ], + [AC_MSG_RESULT([no])]) + +AH_VERBATIM([_GNU_SOURCE], +[/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif]) + +AH_TOP([#ifndef CONFIG_H_INCLUDED + #define CONFIG_H_INCLUDED 1]) +AH_BOTTOM([#endif]) + +# PkgConfig tests +PKG_CHECK_MODULES([CHECK], [check]) +PKG_CHECK_MODULES([SYSTEMD], [libsystemd-daemon]) + +# Checks for header files. +AC_FUNC_ALLOCA +AC_HEADER_STDBOOL +AC_CHECK_HEADERS([attr/xattr.h], [], [AC_MSG_ERROR([Unable to fin xattr headers])]) +AC_CHECK_HEADERS([fcntl.h]) +AC_CHECK_HEADERS([gdbm.h], [], [AC_MSG_ERROR([Unable to find gdbm headers])]) +AC_CHECK_HEADERS([inttypes.h]) +AC_CHECK_HEADERS([limits.h]) +AC_CHECK_HEADERS([locale.h]) +AC_CHECK_HEADERS([malloc.h]) +AC_CHECK_HEADERS([stddef.h]) +AC_CHECK_HEADERS([stdint.h]) +AC_CHECK_HEADERS([stdlib.h]) +AC_CHECK_HEADERS([string.h]) +AC_CHECK_HEADERS([time.h]) +AC_CHECK_HEADERS([math.h]) +AC_CHECK_HEADERS([pthread.h]) +AC_CHECK_HEADERS([sys/param.h]) +AC_CHECK_HEADERS([sys/signalfd.h]) +AC_CHECK_HEADERS([sys/socket.h]) +AC_CHECK_HEADERS([sys/stat.h]) +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_HEADERS([unistd.h]) +AC_CHECK_HEADERS([linux/inotify.h]) +AC_CHECK_FUNC(inotify_init) + +# Options +AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], + [path to systemd system service directory]), [path_systemdsystemunit=${withval}], + [path_systemdsystemunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"]) +SYSTEMD_SYSTEMUNITDIR="${path_systemdsystemunit}" +AC_SUBST(SYSTEMD_SYSTEMUNITDIR) +AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdsystemunit}") + +AC_ARG_WITH([user], AS_HELP_STRING([--with-user=USER], + [user to run buxton as]), [username=${withval}], + [username="buxton"]) +BUXTON_USERNAME="${username}" +AC_SUBST(BUXTON_USERNAME) + +INIPARSER_CFLAGS="-I${srcdir}/src/shared/" +AC_ARG_WITH([local-iniparser], + AS_HELP_STRING([--with-local-iniparser[default=yes]], + [Use built-in iniparser for config parsing]), + [use_local_iniparser="true"], + [PKG_CHECK_MODULES([INIPARSER], [iniparser >= 3.1], + [use_local_iniparser="false"], + [use_local_iniparser="true"; AC_SUBST(INIPARSER_CFLAGS)])]) +AM_CONDITIONAL([USE_LOCAL_INIPARSER], [test x$use_local_iniparser = x"true"]) + +AC_ARG_WITH([module-dir], AS_HELP_STRING([--with-module-dir=MODULEDIR], + [path to buxton modules]), [moduledir=${withval}], + [moduledir="${libdir}/buxton"]) +MODULEDIR="${moduledir}" +AC_SUBST(MODULEDIR) + +AC_ARG_WITH([config-path], AS_HELP_STRING([--with-config-path=CONFPATH], + [path to buxton configuration file]), [confpath=${withval}], + [confpath="${sysconfdir}/buxton.conf"]) +CONFPATH="${confpath}" +AC_SUBST(CONFPATH) + +AC_ARG_WITH([db-path], AS_HELP_STRING([--with-db-path=DBPATH], + [path to buxton db files]), [dbpath=${withval}], + [dbpath="${localstatedir}/lib/buxton"]) +DB_PATH="${dbpath}" +AC_SUBST(DB_PATH) + +AC_ARG_WITH([socket-path], AS_HELP_STRING([--with-socket-path=SOCKETPATH], + [path to buxton socket file]), [socketpath=${withval}], + [socketpath="/run/buxton-0"]) +BUXTON_SOCKET="${socketpath}" +AC_SUBST(BUXTON_SOCKET) + +AC_ARG_WITH([smack-load-file], AS_HELP_STRING([--with-smack-load-file=SMACKLOADFILE], + [path to smack load2 file]), [smack_load_file=${withval}], + [smack_load_file="/sys/fs/smackfs/load2"]) +SMACK_LOAD_FILE="${smack_load_file}" +AC_SUBST(SMACK_LOAD_FILE) + +AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debug mode @<:@default=no@:>@]), + [], [enable_debug=no]) +AS_IF([test "x$enable_debug" = "xyes"], + [AC_DEFINE([DEBUG], [1], [Debugging mode enabled])], + [AC_DEFINE([NDEBUG], [1], [Debugging and assertions disabled])]) +AM_CONDITIONAL([DEBUG], [test x$enable_debug = x"yes"]) + +AC_ARG_ENABLE(manpages, AS_HELP_STRING([--enable-manpages], [enable man pages @<:@default=yes@:>@]), + [], [enable_manpages=yes]) +AS_IF([test "x$enable_manpages" = "xyes"], + [AC_DEFINE([MANPAGE], [1], [Man pages will be included])], + [AC_DEFINE([NMANPAGE], [1], [Man pages will not be included])]) +AM_CONDITIONAL([MANPAGE], [test x$enable_manpages = x"yes"]) + +have_coverage=no +AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage])) +if test "x$enable_coverage" = "xyes" ; then + AC_CHECK_PROG(lcov_found, [lcov], [yes], [no]) + if test "x$lcov_found" = xno ; then + AC_MSG_ERROR([*** lcov support requested but the program was not found]) + else + lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`" + lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`" + if test "$lcov_version_major" -eq 1 -a "$lcov_version_minor" -lt 10; then + AC_MSG_ERROR([*** lcov version is too old. 1.10 required]) + else + have_coverage=yes + AC_DEFINE([COVERAGE], [1], [Coverage enabled]) + fi + fi +fi +AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"]) + +AC_ARG_ENABLE(demos, AS_HELP_STRING([--enable-demos], [enable demos @<:@default=no@:>@]), + [], [enable_demos=no]) +AS_IF([test "x$enable_demos" = "xyes"], + [AC_DEFINE([DEMOS], [1], [Building demos])], + []) +AM_CONDITIONAL([BUILD_DEMOS], [test x$enable_demos = x"yes"]) + +# GTK3 demo +AC_ARG_ENABLE(gtk-demo, AS_HELP_STRING([--enable-gtk-demo], [enable demos @<:@default=no@:>@]), + [], [enable_gtk_demo=no]) +AS_IF([test "x$enable_gtk_demo" = "xyes"], + [AC_DEFINE([DEMOS], [1], [Building GTK demo])], + []) +AM_CONDITIONAL([BUILD_GTK_DEMO], [test x$enable_gtk_demo = x"yes"]) + +if test "x$enable_gtk_demo" = "xyes"; then + # Require GTK3 for client demonstration + PKG_CHECK_MODULES([GTK3], [gtk+-3.0 >= 3.10]) +fi + +AC_CONFIG_COMMANDS([mkdir], [$MKDIR_P test/databases]) +AC_CONFIG_FILES([ +data/buxton.service +data/buxton.socket +data/libbuxton.pc +test/test-pass.ini +test/test-fail.ini +test/test.conf +]) +AC_OUTPUT + +AC_MSG_RESULT([ + buxton $VERSION + ======== + + prefix: ${prefix} + libdir: ${libdir} + sysconfdir: ${sysconfdir} + exec_prefix: ${exec_prefix} + bindir: ${bindir} + sbindir: ${sbindir} + datarootdir: ${datarootdir} + mandir: ${mandir} + modules: ${MODULEDIR} + + compiler: ${CC} + cflags: ${CFLAGS} + ldflags: ${LDFLAGS} + + debug: ${enable_debug} + demos: ${enable_demos} + coverage: ${have_coverage} + manpages: ${enable_manpages} +]) diff --git a/data/buxton.conf b/data/buxton.conf new file mode 100644 index 0000000..e1aad8e --- /dev/null +++ b/data/buxton.conf @@ -0,0 +1,45 @@ +# +# Default buxton config file +# + +[Configuration] +#ModuleDirectory=${libdir}/buxton +#DatabasePath=${localstatedir}/lib/buxton +#SmackLoadFile=/sys/fs/smackfs/load2 +#SocketPath=/run/buxton-0 + +[base] +Type=System +Backend=gdbm +Description=Operating System configuration layer +Priority=0 +# This will end up being a file at @@DB_PATH@@/base.db + +[ro-base] +Type=System +Backend=gdbm +Description=Operating System configuration layer +Priority=0 +#Access=read-only +# This will end up being a file at @@DB_PATH@@/base.db + +[isp] +Type=System +Backend=gdbm +Description=ISP specific settings +Priority=1 +# This will end up being a file at @@DB_PATH@@/isp.db + +[temp] +Type=System +Backend=memory +Priority=99 +Description=A temporary layer for scratch settings and data +# This will not end up in any file + +[user] +Type=User +Backend=gdbm +Priority=1000 +Description=Per-user settings +# This will end up in @@DB_PATH@@/user-.db diff --git a/data/buxton.service.in b/data/buxton.service.in new file mode 100644 index 0000000..d306864 --- /dev/null +++ b/data/buxton.service.in @@ -0,0 +1,9 @@ +[Unit] +Description=Buxton Configuration Service + +[Service] +ExecStart=@prefix@/sbin/buxtond +User=@BUXTON_USERNAME@ + +[Install] +WantedBy=multi-user.target diff --git a/data/buxton.socket.in b/data/buxton.socket.in new file mode 100644 index 0000000..bade73a --- /dev/null +++ b/data/buxton.socket.in @@ -0,0 +1,8 @@ +[Unit] +Description=Buxton Configuration Service + +[Socket] +ListenStream=@BUXTON_SOCKET@ + +[Install] +WantedBy=sockets.target diff --git a/data/libbuxton.pc.in b/data/libbuxton.pc.in new file mode 100644 index 0000000..f731570 --- /dev/null +++ b/data/libbuxton.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: buxton +Description: Library for buxton clients +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lbuxton +Cflags: -I${includedir} diff --git a/demo/gtk_client.c b/demo/gtk_client.c new file mode 100644 index 0000000..8b8105f --- /dev/null +++ b/demo/gtk_client.c @@ -0,0 +1,348 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include + +#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("" + "Using the controls below, you can set a key within the\n" + "user layer. Open another instance of this client to\n" + "check notification support."); + 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("\'test\' value:"); + 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("\'%s\' unset", key_name); + } else { + lab = g_strdup_printf("\'%s\' value: %s", + key_name, (gchar*)value); + } + /* Update UI */ + gtk_label_set_markup(GTK_LABEL(self->value_label), lab); + g_free(lab); + } + + free(value); + free(key_name); + buxton_key_free(key); +} + +/** Main entry */ +int main(int argc, char **argv) +{ + __attribute__ ((unused)) GtkWidget *window = NULL; + + gtk_init(&argc, &argv); + window = buxton_test_new(); + gtk_main(); + + return EXIT_SUCCESS; +} diff --git a/demo/gtk_client.h b/demo/gtk_client.h new file mode 100644 index 0000000..80b23ea --- /dev/null +++ b/demo/gtk_client.h @@ -0,0 +1,49 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include +#include + +typedef struct _BuxtonTest BuxtonTest; +typedef struct _BuxtonTestClass BuxtonTestClass; + +#define BUXTON_TEST_TYPE (buxton_test_get_type()) +#define BUXTON_TEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BUXTON_TEST_TYPE, BuxtonTest)) +#define IS_BUXTON_TEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUXTON_TEST_TYPE)) +#define BUXTON_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BUXTON_TEST_TYPE, BuxtonTestClass)) +#define IS_BUXTON_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BUXTON_TEST_TYPE)) +#define BUXTON_TEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BUXTON_TEST_TYPE, BuxtonTestClass)) + +#define PRIMARY_KEY "test" +#define GROUP "test" +#define LAYER "user" + +GType buxton_test_get_type(void); + +/* BuxtonTest methods */ +GtkWidget* buxton_test_new(void); diff --git a/demo/hellocreategroup.c b/demo/hellocreategroup.c new file mode 100644 index 0000000..0b1c3df --- /dev/null +++ b/demo/hellocreategroup.c @@ -0,0 +1,99 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void create_cb(BuxtonResponse response, void *data) +{ + if (buxton_response_status(response) != 0) { + printf("Failed to create group\n"); + } else { + printf("Created group\n"); + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", NULL, "user", STRING); + if (!key) { + return -1; + } + + if (buxton_create_group(client, key, create_cb, + NULL, false)) { + printf("create group call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/helloget.c b/demo/helloget.c new file mode 100644 index 0000000..27cf132 --- /dev/null +++ b/demo/helloget.c @@ -0,0 +1,107 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void get_cb(BuxtonResponse response, void *data) +{ + int32_t* ret = (int32_t*)data; + + if (buxton_response_status(response) != 0) { + printf("Failed to get value\n"); + return; + } + + *ret = *(int32_t*)buxton_response_value(response); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int32_t gvalue = -1; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_get_value(client, key, get_cb, + &gvalue, false)) { + printf("get call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + if (gvalue >= 0) { + printf("got value: %d\n", gvalue); + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/hellonotify.c b/demo/hellonotify.c new file mode 100644 index 0000000..f191bf4 --- /dev/null +++ b/demo/hellonotify.c @@ -0,0 +1,136 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "buxton.h" + +void notify_cb(BuxtonResponse response, void *data) +{ + bool *status = (bool *)data; + BuxtonKey key; + int32_t *value; + char *name; + + if (buxton_response_status(response) != 0) { + *status = false; + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + + value = (int32_t*)buxton_response_value(response); + if (value) { + printf("key %s updated with new value %d\n", name, *value); + } else { + printf("key %s was removed\n", name); + } + + buxton_key_free(key); + free(value); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + bool status = true; + struct pollfd pfd[1]; + int r; + int fd; + int repoll_count = 10; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", "test", NULL, INT32); + if (!key) { + return -1; + } + + if (buxton_register_notification(client, key, notify_cb, &status, false)) { + printf("set call failed to run\n"); + return -1; + } +repoll: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r < 0) { + printf("poll error\n"); + return -1; + } else if (r == 0) { + if (repoll_count-- > 0) { + goto out; + } + goto repoll; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + if (!status) { + printf("Failed to register for notification\n"); + return -1; + } + + goto repoll; + +out: + if (buxton_unregister_notification(client, key, NULL, NULL, true)) { + printf("Unregistration of notification failed\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/helloremovegroup.c b/demo/helloremovegroup.c new file mode 100644 index 0000000..d1dfc15 --- /dev/null +++ b/demo/helloremovegroup.c @@ -0,0 +1,99 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void remove_cb(BuxtonResponse response, void *data) +{ + if (buxton_response_status(response) != 0) { + printf("Failed to remove group\n"); + } else { + printf("Removed group\n"); + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", NULL, "user", STRING); + if (!key) { + return -1; + } + + if (buxton_remove_group(client, key, remove_cb, + NULL, false)) { + printf("remove group call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/helloset.c b/demo/helloset.c new file mode 100644 index 0000000..cb73103 --- /dev/null +++ b/demo/helloset.c @@ -0,0 +1,110 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void set_cb(BuxtonResponse response, void *data) +{ + BuxtonKey key; + char *name; + + if (buxton_response_status(response) != 0) { + printf("Failed to set value\n"); + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + printf("Set value for key %s\n", name); + buxton_key_free(key); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + int32_t set; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + set = 10; + + if (buxton_set_value(client, key, &set, set_cb, + NULL, false)) { + printf("set call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/hellosetlabel.c b/demo/hellosetlabel.c new file mode 100644 index 0000000..5dc792b --- /dev/null +++ b/demo/hellosetlabel.c @@ -0,0 +1,108 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void set_label_cb(BuxtonResponse response, void *data) +{ + BuxtonKey key; + char *name; + + if (buxton_response_status(response) != 0) { + printf("Failed to set label\n"); + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + printf("Set label for key %s\n", name); + buxton_key_free(key); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + char* label = "label-test"; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_set_label(client, key, label, set_label_cb, + NULL, false)) { + printf("set label call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/hellounset.c b/demo/hellounset.c new file mode 100644 index 0000000..ebff2c4 --- /dev/null +++ b/demo/hellounset.c @@ -0,0 +1,99 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void unset_cb(BuxtonResponse response, void *data) +{ + if (buxton_response_status(response) != 0) { + printf("Failed to unset value\n"); + } else { + printf("Unset value\n"); + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_unset_value(client, key, unset_cb, + NULL, false)) { + printf("unset call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/notifytest.c b/demo/notifytest.c new file mode 100644 index 0000000..16cb63f --- /dev/null +++ b/demo/notifytest.c @@ -0,0 +1,175 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "buxton.h" + +void set_cb(BuxtonResponse response, void *data) +{ + bool *status = (bool *)data; + if (buxton_response_status(response) != 0) { + *status = false; + } else { + *status = true; + } +} + +void notify_cb(BuxtonResponse response, void *data) +{ + bool *status = (bool *)data; + BuxtonKey key; + int32_t *value; + char *name; + + if (buxton_response_status(response) != 0) { + *status = false; + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + + value = (int32_t*)buxton_response_value(response); + if (value) { + printf("key %s updated with new value %d\n", name, *value); + } else { + printf("key %s was removed\n", name); + } + + buxton_key_free(key); + free(value); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key1, key2, key3, key4; + bool status = true; + struct pollfd pfd[1]; + int r; + int fd; + int do_update = 0; + int32_t val = 10; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + + key1 = buxton_key_create("hello", "test1", NULL, INT32); + if (!key1) { + return -1; + } + + key2 = buxton_key_create("hello", "test2", NULL, INT32); + if (!key2) { + return -1; + } + + key3 = buxton_key_create("hello", "test3", NULL, INT32); + if (!key3) { + return -1; + } + + key4 = buxton_key_create("hello", "test1", "user", INT32); + if (!key4) { + return -1; + } + + if (buxton_register_notification(client, key1, notify_cb, &status, false)) { + printf("set call failed to run\n"); + return -1; + } + + if (buxton_register_notification(client, key2, notify_cb, &status, false)) { + printf("set call failed to run\n"); + return -1; + } + + if (buxton_register_notification(client, key3, notify_cb, &status, false)) { + printf("set call failed to run\n"); + return -1; + } + +repoll: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r < 0) { + printf("poll error\n"); + return -1; + } else if (r == 0) { + if (do_update >> 1 == 0) { + val++; + if (buxton_set_value(client, key4, &val, set_cb, &status, false)) { + printf("set value failed\n"); + return -1; + } + } else { + do_update++; + } + goto repoll; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + if (!status) { + printf("Failed to register for notification\n"); + return -1; + } + + goto repoll; + + buxton_key_free(key1); + buxton_key_free(key2); + buxton_key_free(key3); + buxton_close(client); + + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/demo/timing.c b/demo/timing.c new file mode 100644 index 0000000..b374595 --- /dev/null +++ b/demo/timing.c @@ -0,0 +1,312 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" +#include "util.h" + +#define error(...) { printf(__VA_ARGS__); } + +static int iterations = 100000; + +static void callback(BuxtonResponse response, void *userdata) +{ + bool *r; + + if (!userdata) { + return; + } + + r = (bool *)userdata; + + if (buxton_response_status(response) == 0) { + *r = true; + } +} + +enum test_type { + TEST_GET, + TEST_SET, + TEST_SET_UNSET, + TEST_TYPE_MAX +}; + +enum data_type { + TEST_INT32, + TEST_UINT32, + TEST_INT64, + TEST_UINT64, + TEST_BOOLEAN, + TEST_STRING, + TEST_STRING4K, + TEST_FLOAT, + TEST_DOUBLE, + TEST_DATA_TYPE_MAX +}; + +struct testcase { + char name[32]; + enum test_type t; + enum data_type d; +}; + +#define TEST_COUNT (TEST_TYPE_MAX * TEST_DATA_TYPE_MAX) +static struct testcase testcases[TEST_COUNT] = { + { "set_int32", TEST_SET, TEST_INT32 }, + { "get_int32", TEST_GET, TEST_INT32 }, + { "set_unset_int32", TEST_SET_UNSET, TEST_INT32 }, + { "set_uint32", TEST_SET, TEST_UINT32 }, + { "get_uint32", TEST_GET, TEST_UINT32 }, + { "set_unset_uint32", TEST_SET_UNSET, TEST_UINT32 }, + { "set_int64", TEST_SET, TEST_INT64 }, + { "get_int64", TEST_GET, TEST_INT64 }, + { "set_unset_int64", TEST_SET_UNSET, TEST_INT64 }, + { "set_uint64", TEST_SET, TEST_UINT64 }, + { "get_uint64", TEST_GET, TEST_UINT64 }, + { "set_unset_uint64", TEST_SET_UNSET, TEST_UINT64 }, + { "set_boolean", TEST_SET, TEST_BOOLEAN }, + { "get_boolean", TEST_GET, TEST_BOOLEAN }, + { "set_unset_boolean", TEST_SET_UNSET, TEST_BOOLEAN }, + { "set_string", TEST_SET, TEST_STRING }, + { "get_string", TEST_GET, TEST_STRING }, + { "set_unset_string", TEST_SET_UNSET, TEST_STRING }, + { "set_string4k", TEST_SET, TEST_STRING4K }, + { "get_string4k", TEST_GET, TEST_STRING4K }, + { "set_unset_string4k", TEST_SET_UNSET, TEST_STRING4K }, + { "set_float", TEST_SET, TEST_FLOAT }, + { "get_float", TEST_GET, TEST_FLOAT }, + { "set_unset_float", TEST_SET_UNSET, TEST_FLOAT }, + { "set_double", TEST_SET, TEST_DOUBLE }, + { "get_double", TEST_GET, TEST_DOUBLE }, + { "set_unset_double", TEST_SET_UNSET, TEST_DOUBLE } +}; + +static BuxtonClient __client; +static BuxtonData __data; +static BuxtonKey __key; + +static bool init_group(void) +{ + BuxtonKey group; + bool r; + bool d = false; + + group = buxton_key_create("TimingTest", NULL, "user", STRING); + r = buxton_create_group(__client, group, callback, &d, true); + + free(group); + + return (!r && d); +} + +static bool testcase_init(struct testcase *tc) +{ + char name[64] = ""; + int32_t i; + uint32_t ui; + int64_t i6; + uint64_t ui6; + bool b; + char *string; + float f; + double d; + void *value = NULL; + + switch (tc->d) { + case TEST_INT32: + sprintf(name, "TimingTest-%d-int32", getpid()); + __key = buxton_key_create("TimingTest", name, "user", INT32); + i = -672; + value = &i; + break; + case TEST_UINT32: + sprintf(name, "TimingTest-%d-uint32", getpid()); + __key = buxton_key_create("TimingTest", name, "user", UINT32); + ui = 672; + value = &ui; + break; + case TEST_INT64: + sprintf(name, "TimingTest-%d-int64", getpid()); + __key = buxton_key_create("TimingTest", name, "user", INT64); + i6 = -672 * 672; + value = &i6; + break; + case TEST_UINT64: + sprintf(name, "TimingTest-%d-uint64", getpid()); + __key = buxton_key_create("TimingTest", name, "user", UINT64); + ui6 = 672 * 672; + value = &ui6; + break; + case TEST_BOOLEAN: + sprintf(name, "TimingTest-%d-boolean", getpid()); + __key = buxton_key_create("TimingTest", name, "user", BOOLEAN); + b = true; + value = &b; + break; + case TEST_STRING: + sprintf(name, "TimingTest-%d-string", getpid()); + __key = buxton_key_create("TimingTest", name, "user", STRING); + string = "672"; + value = string; + break; + case TEST_STRING4K: + sprintf(name, "TimingTest-%d-string4k", getpid()); + __key = buxton_key_create("TimingTest", name, "user", STRING); + string = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + value = string; + break; + case TEST_FLOAT: + sprintf(name, "TimingTest-%d-float", getpid()); + __key = buxton_key_create("TimingTest", name, "user", FLOAT); + f = (float)3.14; + value = &f; + break; + case TEST_DOUBLE: + sprintf(name, "TimingTest-%d-double", getpid()); + __key = buxton_key_create("TimingTest", name, "user", DOUBLE); + d = 3.14; + value = &d; + break; + default: + return false; + } + + return !buxton_set_value(__client, __key, value, callback, NULL, true); +} + +static bool testcase_cleanup(struct testcase *tc) +{ + bool ret = (!buxton_set_value(__client, __key, &__data, callback, NULL, true) && + !buxton_unset_value(__client, __key, callback, NULL, true)); + buxton_key_free(__key); + return ret; +} + +static bool testcase_run(struct testcase *tc) +{ + bool d = false; + bool r, s; + switch (tc->t) { + case TEST_GET: + r = buxton_get_value(__client, __key, callback, &d, true); + return (!r && d); + case TEST_SET: + r = buxton_set_value(__client, __key, &__data, callback, &d, true); + return (!r && d); + case TEST_SET_UNSET: + r = buxton_set_value(__client, __key, &__data, callback, &d, true); + s = buxton_unset_value(__client, __key, callback, &d, true); + return (!s && !r && d); + default: + return false; + } +} + +static bool timed_func(unsigned long long *elapsed, struct testcase *tc) +{ + struct timespec tsi, tsf; + bool ret; + + clock_gettime(CLOCK_MONOTONIC, &tsi); + ret = testcase_run(tc); + clock_gettime(CLOCK_MONOTONIC, &tsf); + + *elapsed = (unsigned long long)((tsf.tv_nsec - tsi.tv_nsec) + ((tsf.tv_sec - tsi.tv_sec) * 1000000000)); + return ret; +} + +static void test(struct testcase *tc) +{ + unsigned long long elapsed; + unsigned long long total; + unsigned long long errors = 0; + double meansqr; + double mean; + double sigma; + int i; + + total = 0; + meansqr = 0.0; + mean = 0.0; + + testcase_init(tc); + + for (i = 0; i < iterations; i++) { + if (!timed_func(&elapsed, tc)) { + errors++; + } + + mean += (double)elapsed; + meansqr += (double)elapsed * (double)elapsed; + total += elapsed; + } + mean /= (double)iterations; + meansqr /= (double)iterations; + sigma = sqrt(meansqr - (mean * mean)); + + testcase_cleanup(tc); + + printf("%-24s %10.3lfus %10.3lfus %10llu\n", + tc->name, mean / 1000.0, sigma / 1000.0, errors); +} + +int main(int argc, char **argv) +{ + int ret = EXIT_SUCCESS; + int i; + + if (argc == 2) { + iterations = atoi(argv[1]); + if (iterations <= 0) { + exit(EXIT_FAILURE); + } + } else if (argc != 1) { + error("Usage: %s [iterations]\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (!buxton_open(&__client)) { + error("Unable to open BuxtonClient\n"); + exit(EXIT_FAILURE); + } + + init_group(); + + printf("Buxton protocol latency timing tool. Using %i iterations per test.\n", iterations); + printf("Test Name: Average: Sigma: Errors:\n"); + + for (i = 0; i < TEST_COUNT; i++) + test(&testcases[i]); + + buxton_close(__client); + exit(ret); +} + diff --git a/docs/LICENSE.MIT b/docs/LICENSE.MIT new file mode 100644 index 0000000..ad18db0 --- /dev/null +++ b/docs/LICENSE.MIT @@ -0,0 +1,19 @@ +Copyright (c) 2014 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/docs/buxton.7 b/docs/buxton.7 new file mode 100644 index 0000000..1e4ff8c --- /dev/null +++ b/docs/buxton.7 @@ -0,0 +1,69 @@ +'\" t +.TH "BUXTON" "7" "" "buxton 1" "buxton" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton \- A security\-enabled configuration system + +.SH "DESCRIPTION" +.PP +Buxton is a security\-enabled configuration management system\&. It +features a layered approach to configuration storage, with each layer +containing an arbitrary number of groups, each of which may contain +key\-value pairs\&. Mandatory Access Control (MAC) is implemented at +the group level and at the key\-value level\&. + +Currently, buxton uses \m[blue]\fBSmack\fR\m[]\&\s-2\u[1]\d\s+2 to +enforce MAC\&. + +Buxton provides a C library, documented in \fBbuxton\-api\fR(7), for +client applications to use\&. Internally, buxton uses +\fBbuxtond\fR(8) for processing client requests and enforcing +MAC\&. Also, \fBbuxtonctl\fR(1) is provided for interactive use and +for use in shell scripts\&. + +Minimal examples of client API usage are found in +\fBbuxton\-examples\fR(7), and an in\-depth overview of buxton is +found in \fBbuxton\-overview\fR(7)\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[2]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxtonctl\fR(1), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7), +\fBbuxton\-examples\fR(7), +\fBbuxton\-overview\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Smack +.RS 4 +\%https://www.kernel.org/doc/Documentation/security/Smack.txt +.RE +.IP " 2." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton.conf.5 b/docs/buxton.conf.5 new file mode 100644 index 0000000..d3a2485 --- /dev/null +++ b/docs/buxton.conf.5 @@ -0,0 +1,126 @@ +'\" t +.TH "BUXTON\&.CONF" "5" "" "buxton 1" "buxton\&.conf" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton\&.conf \- Configuration of Buxton settings and layer +information + +.SH "SYNOPSIS" +.PP +/etc/buxton\&.conf + +.SH "DESCRIPTION" +.PP +The buxton configuration file can be used to override various paths +used by \fBbuxtonctl\fR(1) and \fBbuxtond\fR(8), as well as the +system\-wide definition of buxton layers\&. + +.SH "OPTIONS" +.PP +Options that are specified in the +"[Configuration]" +section: +.PP +\fIModuleDirectory=\fR +.RS 4 +Sets the directory where backend modules are stored\&. The path is +used by \fBbuxtond\fR(8) to dynamically load the modules as +needed\&. +.RE +.PP +\fIDatabasePath=\fR +.RS 4 +Sets the directory where \fBbuxtond\fR(8) (or \fBbuxtonctl\fR(1) +in direct mode) will store configuration data\&. +.RE +.PP +\fISmackLoadFile=\fR +.RS 4 +Sets the location of the Smack "load2" file from the Smack virtual +filesystem\&. +.RE +.PP +\fISocketPath=\fR +.RS 4 +Sets the path for the Unix Domain Socket used by buxton clients to +communicate with \fBbuxtond\fR(8)\&. +.RE + +.PP +Buxton layers are configured in individual sections of the config +file, where each section name is equivalent to a layer name\&. For +example, the "base" layer configuration is set in the "[base]" +section\&. + +.PP +Options that are specified in each layer section: +.PP +\fIType=\fR +.RS 4 +The type of the layer\&. Accepted values are "System" or "User"\&. +.RE +.PP +\fIBackend=\fR +.RS 4 +The backend to use for the layer\&. Accepted values are "gdbm" or +"memory"\&. Note that the "memory" backend is volatile, so +key\-value pairs will be lost when the \fBbuxtond\fR(8) service +exits\&. +.RE +.PP +\fIPriority=\fR +.RS 4 +The priority of the layer\&. Accepted values are integers greater +than or equal to 0 (zero), where 0 is the lowest\-priority value\&. +.RE +.PP +\fIAccess=\fR +.RS 4 +The access type of the layer\&. Accepted values are "read\-write" and +"read\-only"\&. This is an optional field that defaults to "read\-write"\&. +.RE +.PP +\fIDescription=\fR +.RS 4 +A human\-readable description for the given layer\&. +.RE + +.PP +More details about buxton layers can be found in \fBbuxton\fR(7)\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtonctl\fR(1), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_client_handle_response.3 b/docs/buxton_client_handle_response.3 new file mode 100644 index 0000000..c84aa89 --- /dev/null +++ b/docs/buxton_client_handle_response.3 @@ -0,0 +1,63 @@ +'\" t +.TH "BUXTON_CLIENT_HANDLE_RESPONSE" "3" "buxton 1" "buxton_client_handle_response" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_client_handle_response \- Notification response helper + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +ssize_t buxton_client_handle_response(BuxtonClient \fIclient\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +This function retrieves the response from \fBbuxtond\fR for the \fIclient\fR. + +Several manual pages include code examples that use this function. +For an example, see \fBbuxton_create_group\fR(3). + +.SH "RETURN VALUE" +.PP +Returns the number of messages processed, or -1 if there was an error\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_close.3 b/docs/buxton_close.3 new file mode 100644 index 0000000..f7be2a6 --- /dev/null +++ b/docs/buxton_close.3 @@ -0,0 +1 @@ +.so buxton_open.3 diff --git a/docs/buxton_create_group.3 b/docs/buxton_create_group.3 new file mode 100644 index 0000000..1c74978 --- /dev/null +++ b/docs/buxton_create_group.3 @@ -0,0 +1,235 @@ +'\" t +.TH "BUXTON_CREATE_GROUP" "3" "buxton 1" "buxton_create_group" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_create_group, buxton_remove_group \- Manage groups within buxton + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 +#include +#include +#include + +#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 +#include +#include +#include + +#include "buxton.h" + +void remove_cb(BuxtonResponse response, void *data) +{ + if (buxton_response_status(response) != 0) { + printf("Failed to remove group\\n"); + } else { + printf("Removed group\\n"); + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + key = buxton_key_create("hello", NULL, "user", STRING); + if (!key) { + return -1; + } + + if (buxton_remove_group(client, key, remove_cb, + NULL, false)) { + printf("remove group call failed to run\\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_get_value.3 b/docs/buxton_get_value.3 new file mode 100644 index 0000000..ac8730c --- /dev/null +++ b/docs/buxton_get_value.3 @@ -0,0 +1,158 @@ +'\" t +.TH "BUXTON_GET_VALUE" "3" "buxton 1" "buxton_get_value" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_get_value \- Get the value of a key\-name + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 +#include +#include +#include + +#include "buxton.h" + +void get_cb(BuxtonResponse response, void *data) +{ + int32_t* ret = (int32_t*)data; + + if (buxton_response_status(response) != 0) { + printf("Failed to get value\\n"); + return; + } + + *ret = *(int32_t*)buxton_response_value(response); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int32_t gvalue = -1; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_get_value(client, key, get_cb, + &gvalue, false)) { + printf("get call failed to run\\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + if (gvalue >= 0) { + printf("got value: %d\\n", gvalue); + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_key_create.3 b/docs/buxton_key_create.3 new file mode 100644 index 0000000..928dc0f --- /dev/null +++ b/docs/buxton_key_create.3 @@ -0,0 +1,109 @@ +'\" t +.TH "BUXTON_KEY_CREATE" "3" "buxton 1" "buxton_key_create" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_key_create, buxton_key_free, buxton_key_get_layer, +buxton_key_get_type, buxton_key_get_group, buxton_key_get_name \- +Manage groups and key\-names for buxton clients + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +BuxtonKey buxton_key_create(char *\fIgroup\fB, +.br + char *\fIname\fB, +.br + char *\fIlayer\fB, +.br + BuxtonDataType \fItype\fB) +.sp +.br +char *buxton_key_get_layer(BuxtonKey \fIkey\fB) +.sp +.br +char *buxton_key_get_group(BuxtonKey \fIkey\fB) +.sp +.br +char *buxton_key_get_name(BuxtonKey \fIkey\fB) +.sp +.br +BuxtonDataType buxton_key_get_type(BuxtonKey \fIkey\fB) +.sp +.br +void buxton_key_free(BuxtonKey \fIkey\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +These functions are used by buxton clients to manage groups and +key\-names, both represented by a BuxtonKey\&. BuxtonKeys are +required for all configuration management operations within buxton. + +A BuxtonKey is created with \fBbuxton_key_create\fR(3). Its arguments +serve to initialize the BuxtonKey contents, which include the name of +the \fIlayer\fR, name of the \fIgroup\fR, name of the key \fIname\fR, +and the \fItype\fR of the key name\&. Groups must be initialized by +passing a NULL value for \fIname\fR; similarly, key\-names must be +initialized by passing non\-NULL arguments for both \fIgroup\fR and +\fIname\fR\&. For groups, the \fItype\fR is ignored\&. The function +returns an initialized BuxtonKey\&. + +Note that upon creation, a BuxtonKey is either a group or key\-name, +but not both\&. In other words, if a client needs to a create a group +and key\-name within that group, two BuxtonKeys need to be created\&. + +After creating a BuxtonKey, its layer, group, key name, or key name +type fields can be requested by calling +\fBbuxton_key_get_layer\fR(3), \fBbuxton_key_get_group\fR(3), +\fBbuxton_key_get_name\fR(3), or \fBbuxton_key_get_type\fR(3), +respectively\&. Each of these functions take a single argument, the +BuxtonKey that is being queried, and return the value requested\&. +Note that calling \fBbuxton_key_get_type\fR(3) for a group will +return STRING, since buxton treats groups as strings internally\&. +Calling \fBbuxton_key_get_name\fR(3) for a group will return NULL, +since the name field is not used for groups\&. + +All BuxtonKey's are freed automatically on buxton_close, but can +be freed manually if desired by calling \fBbuxton_key_free\fR(3)\&. +This function takes a single argument, the BuxtonKey to be freed\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_key_free.3 b/docs/buxton_key_free.3 new file mode 100644 index 0000000..04f8c6d --- /dev/null +++ b/docs/buxton_key_free.3 @@ -0,0 +1 @@ +.so buxton_key_create.3 diff --git a/docs/buxton_key_get_group.3 b/docs/buxton_key_get_group.3 new file mode 100644 index 0000000..04f8c6d --- /dev/null +++ b/docs/buxton_key_get_group.3 @@ -0,0 +1 @@ +.so buxton_key_create.3 diff --git a/docs/buxton_key_get_layer.3 b/docs/buxton_key_get_layer.3 new file mode 100644 index 0000000..04f8c6d --- /dev/null +++ b/docs/buxton_key_get_layer.3 @@ -0,0 +1 @@ +.so buxton_key_create.3 diff --git a/docs/buxton_key_get_name.3 b/docs/buxton_key_get_name.3 new file mode 100644 index 0000000..04f8c6d --- /dev/null +++ b/docs/buxton_key_get_name.3 @@ -0,0 +1 @@ +.so buxton_key_create.3 diff --git a/docs/buxton_key_get_type.3 b/docs/buxton_key_get_type.3 new file mode 100644 index 0000000..04f8c6d --- /dev/null +++ b/docs/buxton_key_get_type.3 @@ -0,0 +1 @@ +.so buxton_key_create.3 diff --git a/docs/buxton_open.3 b/docs/buxton_open.3 new file mode 100644 index 0000000..e7a4d83 --- /dev/null +++ b/docs/buxton_open.3 @@ -0,0 +1,95 @@ +'\" t +.TH "BUXTON_OPEN" "3" "buxton 1" "buxton_open" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_open, buxton_close \- Manage buxton client connections + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 + +int main(void) +{ + BuxtonClient client; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + /* Manipulate data, register for notifications, ... */ + + buxton_close(client); + return 0; +} +.fi + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_register_notification.3 b/docs/buxton_register_notification.3 new file mode 100644 index 0000000..b8302e3 --- /dev/null +++ b/docs/buxton_register_notification.3 @@ -0,0 +1,199 @@ +'\" t +.TH "BUXTON_REGISTER_NOTIFICATION" "3" "buxton 1" "buxton_register_notification" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_register_notification, buxton_unregister_notification \- +Manage key-name notifications + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 +#include +#include + +#include "buxton.h" + +void notify_cb(BuxtonResponse response, void *data) +{ + bool *status = (bool *)data; + BuxtonKey key; + int32_t *value; + char *name; + + if (buxton_response_status(response) != 0) { + *status = false; + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + + value = (int32_t*)buxton_response_value(response); + if (value) { + printf("key %s updated with new value %d\\n", name, *value); + } else { + printf("key %s was removed\\n", name); + } + + buxton_key_free(key); + free(value); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + bool status = true; + struct pollfd pfd[1]; + int r; + int fd; + int repoll_count = 10; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + key = buxton_key_create("hello", "test", NULL, INT32); + if (!key) { + return -1; + } + + if (buxton_register_notification(client, key, notify_cb, &status, false)) { + printf("set call failed to run\\n"); + return -1; + } +repoll: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r < 0) { + printf("poll error\\n"); + return -1; + } else if (r == 0) { + if (repoll_count-- > 0) { + goto out; + } + goto repoll; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + if (!status) { + printf("Failed to register for notification\\n"); + return -1; + } + + goto repoll; + +out: + if (buxton_unregister_notification(client, key, NULL, NULL, true)) { + printf("Unregistration of notification failed\\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_remove_group.3 b/docs/buxton_remove_group.3 new file mode 100644 index 0000000..47ef80d --- /dev/null +++ b/docs/buxton_remove_group.3 @@ -0,0 +1 @@ +.so buxton_create_group.3 diff --git a/docs/buxton_response_key.3 b/docs/buxton_response_key.3 new file mode 100644 index 0000000..f1681a0 --- /dev/null +++ b/docs/buxton_response_key.3 @@ -0,0 +1 @@ +.so buxton_response_status.3 diff --git a/docs/buxton_response_status.3 b/docs/buxton_response_status.3 new file mode 100644 index 0000000..5c025f6 --- /dev/null +++ b/docs/buxton_response_status.3 @@ -0,0 +1,94 @@ +'\" t +.TH "BUXTON_RESPONSE_STATUS" "3" "buxton 1" "buxton_response_status" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_response_status, buxton_response_type, buxton_response_key, +buxton_response_value \- Query responses from the buxton daemon + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +int32_t buxton_reponse_status(BuxtonResponse \fIresponse\fB) +.sp +.br +BuxtonControlMessage buxton_reponse_type(BuxtonResponse \fIresponse\fB) +.sp +.br +BuxtonKey buxton_reponse_key(BuxtonResponse \fIresponse\fB) +.sp +.br +void *buxton_reponse_value(BuxtonResponse \fIresponse\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +These functions are used within client-defined buxton callback +functions to query the response returned to the client by the buxton +daemon, \fBbuxtond\fR(8)\&. Callbacks are registered through several +of the API functions, such as \fBbuxton_set_value\fR(3) or +\fBbuxton_get_value\fR(3)\&. + +With a callback function, the client should check the response status +by calling \fBbuxton_response_status\fR(3), with the \fIresponse\fR +argument passed to the callback\&. This function returns 0 on +success, or a non-zero value on failure\&. + +Next, the client will want to check the type of response received +from the daemon by calling \fBbuxton_response_type\fR(3)\&. The type +will correspond to the type of operation that the client originally +requested\&. There are several possible return values, depending on +the type of operation; for operations that manipulate or query +configuration, the values include BUXTON_CONTROL_SET, +BUXTON_CONTROL_SET_LABEL, BUXTON_CONTROL_CREATE_GROUP, +BUXTON_CONTROL_REMOVE_GROUP, BUXTON_CONTROL_GET, and +BUXTON_CONTROL_UNSET; for operations related to notifications, the +values include BUXTON_CONTROL_NOTIFY and BUXTON_CONTROL_UNNOTIFY\&. + +Finally, the client can validate that the configuration action +requested by the client matches the action taken by the daemon\&. To +query the BuxtonKey operated on by the daemon, the client should call +\fBbuxton_response_key\fR(3), which returns the BuxtonKey in +question\&. To query the value acted on by the daemon for this +BuxtonKey, a call to \fBbuxton_response_value\fR(3) returns an +untyped pointer to this value\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_response_type.3 b/docs/buxton_response_type.3 new file mode 100644 index 0000000..f1681a0 --- /dev/null +++ b/docs/buxton_response_type.3 @@ -0,0 +1 @@ +.so buxton_response_status.3 diff --git a/docs/buxton_response_value.3 b/docs/buxton_response_value.3 new file mode 100644 index 0000000..f1681a0 --- /dev/null +++ b/docs/buxton_response_value.3 @@ -0,0 +1 @@ +.so buxton_response_status.3 diff --git a/docs/buxton_set_conf_file.3 b/docs/buxton_set_conf_file.3 new file mode 100644 index 0000000..edac44f --- /dev/null +++ b/docs/buxton_set_conf_file.3 @@ -0,0 +1,92 @@ +'\" t +.TH "BUXTON_SET_CONF_FILE" "3" "buxton 1" "buxton_set_conf_file" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_set_conf_file \- Set the path to the Buxton configuration file + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 + +int main(void) +{ + BuxtonClient client; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + if (!buxton_set_conf_file("/etc/buxton-custom.conf")) { + printf("failed to set conf file\\n"); + return -1; + } + + buxton_close(client); + return 0; +} + +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\&.conf\fR(5), +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_set_label.3 b/docs/buxton_set_label.3 new file mode 100644 index 0000000..6f47570 --- /dev/null +++ b/docs/buxton_set_label.3 @@ -0,0 +1,159 @@ +'\" t +.TH "BUXTON_SET_LABEL" "3" "buxton 1" "buxton_set_label" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_set_label \- Set the Smack label for a group or key + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 +#include +#include +#include + +#include "buxton.h" + +void set_label_cb(BuxtonResponse response, void *data) +{ + BuxtonKey key; + char *name; + + if (buxton_response_status(response) != 0) { + printf("Failed to set label\\n"); + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + printf("Set label for key %s\\n", name); + buxton_key_free(key); + free(name); +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + char* label = "label-test"; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_set_label(client, key, label, set_label_cb, + NULL, false)) { + printf("set label call failed to run\\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_set_value.3 b/docs/buxton_set_value.3 new file mode 100644 index 0000000..3717ba3 --- /dev/null +++ b/docs/buxton_set_value.3 @@ -0,0 +1,246 @@ +'\" t +.TH "BUXTON_SET_VALUE" "3" "buxton 1" "buxton_set_value" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_set_value, buxton_unset_value \- Modify values for BuxtonKeys + +.SH "SYNOPSIS" +.nf +\fB +#include +\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 +#include +#include +#include + +#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 +#include +#include +#include + +#include "buxton.h" + +void unset_cb(BuxtonResponse response, void *data) +{ + if (buxton_response_status(response) != 0) { + printf("Failed to unset value\\n"); + } else { + printf("Unset value\\n"); + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + int fd; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + + key = buxton_key_create("hello", "test", "user", INT32); + if (!key) { + return -1; + } + + if (buxton_unset_value(client, key, unset_cb, + NULL, false)) { + printf("unset call failed to run\\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + buxton_key_free(key); + buxton_close(client); + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxton_unregister_notification.3 b/docs/buxton_unregister_notification.3 new file mode 100644 index 0000000..f3ba4e8 --- /dev/null +++ b/docs/buxton_unregister_notification.3 @@ -0,0 +1 @@ +.so buxton_register_notification.3 diff --git a/docs/buxton_unset_value.3 b/docs/buxton_unset_value.3 new file mode 100644 index 0000000..d1eecca --- /dev/null +++ b/docs/buxton_unset_value.3 @@ -0,0 +1 @@ +.so buxton_set_value.3 diff --git a/docs/buxtonctl.1 b/docs/buxtonctl.1 new file mode 100644 index 0000000..3dd95a9 --- /dev/null +++ b/docs/buxtonctl.1 @@ -0,0 +1,228 @@ +'\" t +.TH "BUXTONCTL" "1" "" "buxton 1" "buxtonctl" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxtonctl \- Manipulates data stored in the buxton configuration +system + +.SH "SYNOPSIS" +.HP \w'\fBbuxtonctl\fR\ 'u +\fBbuxtonctl\fR [OPTIONS...] COMMAND ARGS + +.SH "DESCRIPTION" +.PP +\fBbuxtonctl\fR +is used to modify and query configuration, as managed by +\fBbuxton\fR(7)\&. + +.SH "OPTIONS" +.PP +The following options are understood: +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Prints a help message\&. +.RE +.PP +\fB\-d\fR, \fB\-\-direct\fR +.RS 4 +Modifies the configuration directly, without connecting to +\fBbuxtond\fR(8)\&. Note that this is a privileged operation\&. +.RE +.PP +\fB\-c\fR FILE, \fB\-\-config\-file\fR FILE +.RS 4 +Path to a buxton configuration file (see \fBbuxton\&.conf\fR(5))\&. +.RE + +.SH "COMMANDS" +.PP +The following commands are understood: +.SS "Group manipulation" +.PP +\fBcreate\-group\fR LAYER GROUP +.RS 4 +Creates a group within the specified layer\&. +.RE +.PP +\fBset\-label\fR LAYER GROUP LABEL +.RS 4 +Sets the Smack label for the group in the specified layer\&. Note +that this is a privileged operation\&. +.RE +.PP +\fBremove\-group\fR LAYER GROUP +.RS 4 +Removes a group within the specified layer\&. Note that this +operation recursively removes all keys within the given group\&. +.RE +.SS "Key manipulation" +.PP +Note that all "get" commands accept an optional LAYER argument\&. +Without specifying the layer, the returned value will be taken from +the layer with highest priority that the client has permission to +read\&. With the layer argument, buxton will try to return the key +value only from the given layer\&. +.PP +\fBget\-string\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with string type\&. +.RE +.PP +\fBset\-string\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with string type\&. +.RE +.PP +\fBget\-int32\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with int32_t type\&. +.RE +.PP +\fBset\-int32\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with string type\&. +.RE +.PP +\fBget\-uint32\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with uint32_t type\&. +.RE +.PP +\fBset\-uint32\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with uint32_t type\&. +.RE +.PP +\fBget\-int64\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with int64_t type\&. +.RE +.PP +\fBset\-int64\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with int64_t type\&. +.RE +.PP +\fBget\-uint64\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with uint64_t type\&. +.RE +.PP +\fBset\-uint64\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with uint64_t type\&. +.RE +.PP +\fBget\-float\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with float type\&. +.RE +.PP +\fBset\-float\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with float type\&. +.RE +.PP +\fBget\-double\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with double type\&. +.RE +.PP +\fBset\-double\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with double type\&. +.RE +.PP +\fBget\-bool\fR [LAYER] GROUP KEY +.RS 4 +Gets a key value with boolean type\&. +.RE +.PP +\fBset\-bool\fR LAYER GROUP KEY VALUE +.RS 4 +Sets a key value with boolean type\&. +.RE +.PP +\fBset\-label\fR LAYER GROUP KEY LABEL +.RS 4 +Sets the Smack label on a key\&. Note that this is a privileged +operation\&. +.RE +.PP +\fBunset\-value\fR LAYER GROUP KEY +.RS 4 +Unset the value on a key\&. This removes the key from the given +group\&. +.RE + +.SH "ENVIRONMENT VARIABLES" +.PP +\fI$BUXTON_CONF_FILE\fR +.RS 4 +The path to a buxton configuration file (see +\fBbuxton\&.conf\fR(5))\&. +.RE +.PP +\fI$BUXTON_MODULE_DIR\fR +.RS 4 +The directory in which buxton's backend modules reside\&. +.RE +.PP +\fI$BUXTON_DB_PATH\fR +.RS 4 +The directory that buxtond will use for configuration storage\&. +.RE +.PP +\fI$BUXTON_SMACK_LOAD_FILE\fR +.RS 4 +The path to the Smack "load2" file, typically residing on the Smack +virtual filesystem (smackfs)\&. +.RE +.PP +\fI$BUXTON_BUXTON_SOCKET\fR +.RS 4 +The path to the Unix Domain Socket used by buxton clients to +communicate with buxtond\&. +.RE + +.SH "EXIT STATUS" +.PP +On success, 0 is returned, a non\-zero failure code otherwise\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7), +\fBbuxton\&.conf\fR(5) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxtond.8 b/docs/buxtond.8 new file mode 100644 index 0000000..0bd8042 --- /dev/null +++ b/docs/buxtond.8 @@ -0,0 +1,95 @@ +'\" t +.TH "BUXTOND" "8" "" "buxton 1" "buxtond" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxtond \- The buxton daemon + +.SH "SYNOPSIS" +.HP \w'\fBbuxtond\fR\ 'u +\fBbuxtond\fR [OPTIONS...] + +.SH "DESCRIPTION" +.PP +\fBbuxtond\fR is the system service used by buxton to handle client +requests and enforce Mandatory Access Control\&. + +.SH "OPTIONS" +.PP +The following options are understood: +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Prints a help message\&. +.RE +.PP +\fB\-c\fR FILE, \fB\-\-config\-file\fR FILE +.RS 4 +Path to a buxton configuration file (see \fBbuxton\&.conf\fR(5))\&. +.RE + +.SH "ENVIRONMENT VARIABLES" +.PP +\fI$BUXTON_CONF_FILE\fR +.RS 4 +The path to a buxton configuration file (see +\fBbuxton\&.conf\fR(5))\&. +.RE +.PP +\fI$BUXTON_MODULE_DIR\fR +.RS 4 +The directory in which buxton's backend modules reside\&. +.RE +.PP +\fI$BUXTON_DB_PATH\fR +.RS 4 +The directory that buxtond will use for configuration storage\&. +.RE +.PP +\fI$BUXTON_SMACK_LOAD_FILE\fR +.RS 4 +The path to the Smack "load2" file, typically residing on the Smack +virtual filesystem (smackfs)\&. +.RE +.PP +\fI$BUXTON_BUXTON_SOCKET\fR +.RS 4 +The path to the Unix Domain Socket used by buxton clients to +communicate with buxtond\&. +.RE + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtonctl\fR(1), +\fBbuxton\-api\fR(7), +\fBbuxton\&.conf\fR(5) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/m4/.empty b/m4/.empty new file mode 100644 index 0000000..e69de29 diff --git a/packaging/buxton.manifest b/packaging/buxton.manifest new file mode 100644 index 0000000..1a9c577 --- /dev/null +++ b/packaging/buxton.manifest @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packaging/buxton.spec b/packaging/buxton.spec new file mode 100644 index 0000000..de0c081 --- /dev/null +++ b/packaging/buxton.spec @@ -0,0 +1,82 @@ +Name: buxton +Version: 2 +Release: 1 +License: LGPL-2.1 +Group: Framework/system +Summary: buxton +Source0: %{name}-%{version}.tar.bz2 +Source1001: %{name}.manifest + +BuildRequires: pkgconfig(check) +BuildRequires: pkgconfig(libsystemd-daemon) +BuildRequires: gdbm-devel +BuildRequires: libattr-devel +Requires: %{name}-libs = %{version}-%{release} +Requires: gdbm + +%description +buxton + +%package libs +Summary: buxton libraries +License: LGPL-2.1 + +%description libs +buxton devel + +%package devel +Summary: buxton devel +License: LGPL-2.1 +Requires: %{name} = %{version}-%{release} + +%description devel +buxton devel + +%prep +%setup -q + +%build +cp %{SOURCE1001} . +./autogen.sh --with-user=root --disable-manpages +make %{?_smp_mflags} + +%install +%make_install + +# Make sure this directory exist +mkdir -p %{buildroot}%{_sharedstatedir}/buxton + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/license +cat LICENSE.LGPL2.1 > $RPM_BUILD_ROOT%{_datadir}/license/buxton +cat LICENSE.LGPL2.1 > $RPM_BUILD_ROOT%{_datadir}/license/buxton-libs + +%post +mkdir -p %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d +ln -s %{_libdir}/systemd/system/buxton.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/ + +%files +%defattr(-,root,root,-) +%{_datadir}/license/buxton +%dir %{_sharedstatedir}/buxton +%{_sysconfdir}/buxton.conf +%{_bindir}/buxtonctl +%{_libdir}/systemd/system/buxton.service +%{_libdir}/systemd/system/buxton.socket +%{_libdir}/systemd/system/sockets.target.wants/buxton.socket +%{_sbindir}/buxtond +%manifest %{name}.manifest + +%files libs +%defattr(-,root,root,-) +%{_datadir}/license/buxton-libs +%{_libdir}/libbuxton.so.* +%{_libdir}/buxton/gdbm.so +%{_libdir}/buxton/memory.so +%manifest %{name}.manifest + +%files devel +%defattr(-,root,root,-) +%{_includedir}/buxton.h +%{_libdir}/libbuxton.so +%{_libdir}/pkgconfig/libbuxton.pc +%manifest %{name}.manifest diff --git a/src/cli/client.c b/src/cli/client.c new file mode 100644 index 0000000..decb4dd --- /dev/null +++ b/src/cli/client.c @@ -0,0 +1,586 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include + +#include "buxton.h" +#include "buxtonarray.h" +#include "buxtonresponse.h" +#include "client.h" +#include "direct.h" +#include "hashmap.h" +#include "protocol.h" +#include "util.h" + +static char *nv(char *s) +{ + if (s) { + return s; + } + return "(null)"; +} + +bool cli_create_db(BuxtonControl *control, + __attribute__((unused)) BuxtonDataType type, + char *one, + __attribute__((unused)) char *two, + __attribute__((unused)) char *three, + __attribute__((unused)) char *four) +{ + BuxtonString layer_name; + bool ret; + + if (!control->client.direct) { + printf("Unable to create db in non direct mode\n"); + } + + layer_name = buxton_string_pack(one); + + ret = buxton_direct_init_db(control, &layer_name); + + return ret; +} + +bool cli_set_label(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, char *four) +{ + BuxtonString label; + BuxtonKey key; + bool ret = false; + + if (four != NULL) { + key = buxton_key_create(two, three, one, type); + } else { + key = buxton_key_create(two, NULL, one, type); + } + + if (!key) { + return ret; + } + + if (four != NULL) { + label = buxton_string_pack(four); + } else { + label = buxton_string_pack(three); + } + + if (control->client.direct) { + ret = buxton_direct_set_label(control, (_BuxtonKey *)key, &label); + } else { + ret = !buxton_set_label(&control->client, key, label.value, + NULL, NULL, true); + } + + if (!ret) { + char *name = get_name(key); + printf("Failed to update key \'%s:%s\' label in layer '%s'\n", + two, nv(name), one); + free(name); + } + buxton_key_free(key); + return ret; +} + +bool cli_create_group(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, char *four) +{ + BuxtonKey key; + bool ret = false; + + key = buxton_key_create(two, NULL, one, type); + if (!key) { + return ret; + } + + if (control->client.direct) { + ret = buxton_direct_create_group(control, (_BuxtonKey *)key, NULL); + } else { + ret = !buxton_create_group(&control->client, key, NULL, NULL, true); + } + + if (!ret) { + char *group = get_group(key); + printf("Failed to create group \'%s\' in layer '%s'\n", + nv(group), one); + free(group); + } + buxton_key_free(key); + return ret; +} + +bool cli_remove_group(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, char *four) +{ + BuxtonKey key; + bool ret = false; + + key = buxton_key_create(two, NULL, one, type); + if (!key) { + return ret; + } + + if (control->client.direct) { + ret = buxton_direct_remove_group(control, (_BuxtonKey *)key, NULL); + } else { + ret = !buxton_remove_group(&control->client, key, NULL, NULL, true); + } + + if (!ret) { + char *group = get_group(key); + printf("Failed to remove group \'%s\' in layer '%s'\n", + nv(group), one); + free(group); + } + buxton_key_free(key); + return ret; +} + +bool cli_get_label(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, + __attribute__((unused)) char *four) +{ + /* Not yet implemented */ + return false; +} + +bool cli_set_value(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, char *four) +{ + BuxtonString value; + BuxtonKey key; + BuxtonData set; + bool ret = false; + + memzero((void*)&set, sizeof(BuxtonData)); + key = buxton_key_create(two, three, one, type); + if (!key) { + return ret; + } + + value.value = four; + value.length = (uint32_t)strlen(four) + 1; + + set.type = type; + switch (set.type) { + case STRING: + set.store.d_string.value = value.value; + set.store.d_string.length = value.length; + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + four, NULL, NULL, true); + } + break; + case INT32: + set.store.d_int32 = (int32_t)strtol(four, NULL, 10); + if (errno) { + printf("Invalid int32_t value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_int32, NULL, + NULL, true); + } + break; + case UINT32: + set.store.d_uint32 = (uint32_t)strtol(value.value, NULL, 10); + if (errno) { + printf("Invalid uint32_t value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_uint32, NULL, + NULL, true); + } + break; + case INT64: + set.store.d_int64 = strtoll(value.value, NULL, 10); + if (errno) { + printf("Invalid int64_t value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_int64, NULL, + NULL, true); + } + break; + case UINT64: + set.store.d_uint64 = strtoull(value.value, NULL, 10); + if (errno) { + printf("Invalid uint64_t value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_uint64, NULL, + NULL, true); + } + break; + case FLOAT: + set.store.d_float = strtof(value.value, NULL); + if (errno) { + printf("Invalid float value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_float, NULL, + NULL, true); + } + break; + case DOUBLE: + set.store.d_double = strtod(value.value, NULL); + if (errno) { + printf("Invalid double value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_double, NULL, + NULL, true); + } + break; + case BOOLEAN: + if (strcaseeq(value.value, "true") || + strcaseeq(value.value, "on") || + strcaseeq(value.value, "enable") || + strcaseeq(value.value, "yes") || + strcaseeq(value.value, "y") || + strcaseeq(value.value, "t") || + strcaseeq(value.value, "1")) { + set.store.d_boolean = true; + } else if (strcaseeq(value.value, "false") || + strcaseeq(value.value, "off") || + strcaseeq(value.value, "disable") || + strcaseeq(value.value, "no") || + strcaseeq(value.value, "n") || + strcaseeq(value.value, "f") || + strcaseeq(value.value, "0")) { + set.store.d_boolean = false; + } else { + printf("Invalid bool value\n"); + return ret; + } + if (control->client.direct) { + ret = buxton_direct_set_value(control, + (_BuxtonKey *)key, + &set, NULL); + } else { + ret = !buxton_set_value(&control->client, key, + &set.store.d_boolean, + NULL, NULL, true); + } + break; + default: + break; + } + + if (!ret) { + char *group = get_group(key); + char *name = get_name(key); + char *layer = get_layer(key); + + printf("Failed to update key \'%s:%s\' in layer '%s'\n", + nv(group), nv(name), nv(layer)); + free(group); + free(name); + free(layer); + } + + return ret; +} + +void get_value_callback(BuxtonResponse response, void *data) +{ + BuxtonKey key; + BuxtonData *r = (BuxtonData *)data; + void *p; + + if (buxton_response_status(response) != 0) { + return; + } + + p = buxton_response_value(response); + if (!p) { + return; + } + key = buxton_response_key(response); + if (!key) { + free(p); + return; + } + + switch (buxton_key_get_type(key)) { + case STRING: + r->store.d_string.value = (char *)p; + r->store.d_string.length = (uint32_t)strlen(r->store.d_string.value) + 1; + r->type = STRING; + break; + case INT32: + r->store.d_int32 = *(int32_t *)p; + r->type = INT32; + break; + case UINT32: + r->store.d_uint32 = *(uint32_t *)p; + r->type = UINT32; + break; + case INT64: + r->store.d_int64 = *(int64_t *)p; + r->type = INT64; + break; + case UINT64: + r->store.d_uint64 = *(uint64_t *)p; + r->type = UINT64; + break; + case FLOAT: + r->store.d_float = *(float *)p; + r->type = FLOAT; + break; + case DOUBLE: + memcpy(&r->store.d_double, p, sizeof(double)); + r->type = DOUBLE; + break; + case BOOLEAN: + r->store.d_boolean = *(bool *)p; + r->type = BOOLEAN; + break; + default: + break; + } + + if (buxton_key_get_type(key) != STRING) { + free(p); + } + free(key); +} + +bool cli_get_value(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, __attribute__((unused)) char * four) +{ + BuxtonKey key; + BuxtonData get; + _cleanup_free_ char *prefix = NULL; + _cleanup_free_ char *group = NULL; + _cleanup_free_ char *name = NULL; + BuxtonString dlabel; + bool ret = false; + int32_t ret_val; + int r; + + memzero((void*)&get, sizeof(BuxtonData)); + if (three != NULL) { + key = buxton_key_create(two, three, one, type); + r = asprintf(&prefix, "[%s] ", one); + if (!r) { + abort(); + } + } else { + key = buxton_key_create(one, two, NULL, type); + r = asprintf(&prefix, " "); + if (!r) { + abort(); + } + } + + if (!key) { + return false; + } + + if (three != NULL) { + if (control->client.direct) { + ret = buxton_direct_get_value_for_layer(control, key, + &get, &dlabel, + NULL); + } else { + ret = buxton_get_value(&control->client, + key, + get_value_callback, + &get, true); + } + if (ret) { + group = get_group(key); + name = get_name(key); + printf("Requested key was not found in layer \'%s\': %s:%s\n", + one, nv(group), nv(name)); + return false; + } + } else { + if (control->client.direct) { + ret_val = buxton_direct_get_value(control, key, &get, &dlabel, NULL); + if (ret_val == 0) { + ret = true; + } + } else { + ret = buxton_get_value(&control->client, key, + get_value_callback, &get, + true); + } + if (ret) { + group = get_group(key); + name = get_name(key); + printf("Requested key was not found: %s:%s\n", nv(group), + nv(name)); + return false; + } + } + + group = get_group(key); + name = get_name(key); + switch (get.type) { + case STRING: + printf("%s%s:%s = %s\n", prefix, nv(group), nv(name), + get.store.d_string.value ? get.store.d_string.value : ""); + break; + case INT32: + printf("%s%s:%s = %" PRId32 "\n", prefix, nv(group), + nv(name), get.store.d_int32); + break; + case UINT32: + printf("%s%s:%s = %" PRIu32 "\n", prefix, nv(group), + nv(name), get.store.d_uint32); + break; + case INT64: + printf("%s%s:%s = %" PRId64 "\n", prefix, nv(group), + nv(name), get.store.d_int64); + break; + case UINT64: + printf("%s%s:%s = %" PRIu64 "\n", prefix, nv(group), + nv(name), get.store.d_uint64); + break; + case FLOAT: + printf("%s%s:%s = %f\n", prefix, nv(group), + nv(name), get.store.d_float); + break; + case DOUBLE: + printf("%s%s:%s = %f\n", prefix, nv(group), + nv(name), get.store.d_double); + break; + case BOOLEAN: + if (get.store.d_boolean == true) { + printf("%s%s:%s = true\n", prefix, nv(group), + nv(name)); + } else { + printf("%s%s:%s = false\n", prefix, nv(group), + nv(name)); + } + break; + case BUXTON_TYPE_MIN: + printf("Requested key was not found: %s:%s\n", nv(group), + nv(name)); + return false; + default: + printf("unknown type\n"); + return false; + } + + if (get.type == STRING) { + free(get.store.d_string.value); + } + return true; +} + +bool cli_list_keys(BuxtonControl *control, + __attribute__((unused))BuxtonDataType type, + char *one, char *two, char *three, + __attribute__((unused)) char *four) +{ + /* not yet implemented */ + return false; +} + +void unset_value_callback(BuxtonResponse response, void *data) +{ + BuxtonKey key = buxton_response_key(response); + char *group, *name; + + if (!key) { + return; + } + + group = buxton_key_get_group(key); + name = buxton_key_get_name(key); + printf("unset key %s:%s\n", nv(group), nv(name)); + + free(group); + free(name); + buxton_key_free(key); +} + +bool cli_unset_value(BuxtonControl *control, + BuxtonDataType type, + char *one, char *two, char *three, + __attribute__((unused)) char *four) +{ + BuxtonKey key; + + key = buxton_key_create(two, three, one, type); + + if (!key) { + return false; + } + + if (control->client.direct) { + return buxton_direct_unset_value(control, key, NULL); + } else { + return !buxton_unset_value(&control->client, + key, unset_value_callback, + NULL, true); + } +} +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/cli/client.h b/src/cli/client.h new file mode 100644 index 0000000..05adc36 --- /dev/null +++ b/src/cli/client.h @@ -0,0 +1,203 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file client.h Internal header + * This file is used internally by buxton to provide functionality + * used for buxtonctl + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "backend.h" +#include "hashmap.h" + +/** + * Store a command reference in Buxton CLI + * @param type Type of data to operate on + * @param one Pointer to char for first parameter + * @param two Pointer to char for second parameter + * @param three Pointer to char for third parameter + * @param four Pointer to char for fourth parameter + * @return a boolean value, indicating success of the operation + */ +typedef bool (*command_method) (BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, + char *four); + +/** + * Defines a command within the buxtonctl cli + */ +typedef struct Command { + const char *name; /** +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "backend.h" +#include "client.h" +#include "configurator.h" +#include "direct.h" +#include "hashmap.h" +#include "protocol.h" +#include "util.h" + +static Hashmap *commands; +static BuxtonControl control; + +static void print_version(void) +{ + printf("buxtonctl " PACKAGE_VERSION "\n" + "Copyright (C) 2013 Intel Corporation\n" + "buxton is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU Lesser General Public License as\n" + "published by the Free Software Foundation; either version 2.1\n" + "of the License, or (at your option) any later version.\n"); +} + +static bool print_help(void) +{ + const char *key; + Iterator iterator; + Command *command; + + printf("buxtonctl: Usage\n\n"); + + HASHMAP_FOREACH_KEY(command, key, commands, iterator) { + printf("\t%12s - %s\n", key, command->description); + }; + + return true; +} + +static void print_usage(Command *command) +{ + if (command->min_arguments == command->max_arguments) { + printf("%s takes %d arguments - %s\n", command->name, command->min_arguments, command->usage); + } else { + printf("%s takes at least %d arguments - %s\n", command->name, command->min_arguments, command->usage); + } +} + +/** + * Entry point into buxtonctl + * @param argc Number of arguments passed + * @param argv An array of string arguments + * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE + */ +int main(int argc, char **argv) +{ + bool ret = false; + Command c_get_string, c_set_string; + Command c_get_int32, c_set_int32; + Command c_get_uint32, c_set_uint32; + Command c_get_int64, c_set_int64; + Command c_get_uint64, c_set_uint64; + Command c_get_float, c_set_float; + Command c_get_double, c_set_double; + Command c_get_bool, c_set_bool; + Command c_set_label; + Command c_create_group, c_remove_group; + Command c_unset_value; + Command c_create_db; + Command *command; + int i = 0; + int c; + bool help = false; + bool version = false; + control.client.direct = false; + char *conf_path = NULL; + BuxtonClient client = NULL; + + /* libtool bites my twinkie */ + include_configurator(); + include_protocol(); + include_serialize(); + + /* Build a command list */ + commands = hashmap_new(string_hash_func, string_compare_func); + if (!commands) { + exit(EXIT_FAILURE); + } + + /* Strings */ + c_get_string = (Command) { "get-string", "Get a string value by key", + 2, 3, "[layer] group name", &cli_get_value, STRING }; + hashmap_put(commands, c_get_string.name, &c_get_string); + + c_set_string = (Command) { "set-string", "Set a key with a string value", + 4, 4, "layer group name value", &cli_set_value, STRING }; + hashmap_put(commands, c_set_string.name, &c_set_string); + + /* 32bit Integers */ + c_get_int32 = (Command) { "get-int32", "Get an int32_t value by key", + 2, 3, "[layer] group name", &cli_get_value, INT32 }; + hashmap_put(commands, c_get_int32.name, &c_get_int32); + + c_set_int32 = (Command) { "set-int32", "Set a key with an int32_t value", + 4, 4, "layer group name value", &cli_set_value, INT32 }; + hashmap_put(commands, c_set_int32.name, &c_set_int32); + + /* Unsigned 32bit Integers */ + c_get_uint32 = (Command) { "get-uint32", "Get an uint32_t value by key", + 2, 3, "[layer] group name", &cli_get_value, UINT32 }; + hashmap_put(commands, c_get_uint32.name, &c_get_uint32); + + c_set_uint32 = (Command) { "set-uint32", "Set a key with an uint32_t value", + 4, 4, "layer group name value", &cli_set_value, UINT32 }; + hashmap_put(commands, c_set_uint32.name, &c_set_uint32); + + /* 32bit Integers */ + c_get_int64 = (Command) { "get-int64", "Get an int64_t value by key", + 2, 3, "[layer] group name", &cli_get_value, INT64}; + hashmap_put(commands, c_get_int64.name, &c_get_int64); + + c_set_int64 = (Command) { "set-int64", "Set a key with an int64_t value", + 4, 4, "layer group name value", &cli_set_value, INT64 }; + hashmap_put(commands, c_set_int64.name, &c_set_int64); + + /* Unsigned 32bit Integers */ + c_get_uint64 = (Command) { "get-uint64", "Get an uint64_t value by key", + 2, 3, "[layer] group name", &cli_get_value, UINT64}; + hashmap_put(commands, c_get_uint64.name, &c_get_uint64); + + c_set_uint64 = (Command) { "set-uint64", "Set a key with an uint64_t value", + 4, 4, "layer group name value", &cli_set_value, UINT64 }; + hashmap_put(commands, c_set_uint64.name, &c_set_uint64); + + /* Floats */ + c_get_float = (Command) { "get-float", "Get a float point value by key", + 2, 3, "[layer] group name", &cli_get_value, FLOAT }; + hashmap_put(commands, c_get_float.name, &c_get_float); + + c_set_float = (Command) { "set-float", "Set a key with a floating point value", + 4, 4, "layer group name value", &cli_set_value, FLOAT }; + hashmap_put(commands, c_set_float.name, &c_set_float); + + /* Doubles */ + c_get_double = (Command) { "get-double", "Get a double precision value by key", + 2, 3, "[layer] group name", &cli_get_value, DOUBLE }; + hashmap_put(commands, c_get_double.name, &c_get_double); + + c_set_double = (Command) { "set-double", "Set a key with a double precision value", + 4, 4, "layer group name value", &cli_set_value, DOUBLE }; + hashmap_put(commands, c_set_double.name, &c_set_double); + + /* Booleans */ + c_get_bool = (Command) { "get-bool", "Get a boolean value by key", + 2, 3, "[layer] group name", &cli_get_value, BOOLEAN }; + hashmap_put(commands, c_get_bool.name, &c_get_bool); + + c_set_bool = (Command) { "set-bool", "Set a key with a boolean value", + 4, 4, "layer group name value", &cli_set_value, BOOLEAN }; + hashmap_put(commands, c_set_bool.name, &c_set_bool); + + /* SMACK labels */ + c_set_label = (Command) { "set-label", "Set a value's label", + 3, 4, "layer group [name] label", &cli_set_label, STRING }; + + hashmap_put(commands, c_set_label.name, &c_set_label); + + /* Group management */ + c_create_group = (Command) { "create-group", "Create a group in a layer", + 2, 2, "layer group", &cli_create_group, STRING }; + hashmap_put(commands, c_create_group.name, &c_create_group); + + c_remove_group = (Command) { "remove-group", "Remove a group from a layer", + 2, 2, "layer group", &cli_remove_group, STRING }; + hashmap_put(commands, c_remove_group.name, &c_remove_group); + + /* Unset value */ + c_unset_value = (Command) { "unset-value", "Unset a value by key", + 3, 3, "layer group name", &cli_unset_value, STRING }; + hashmap_put(commands, c_unset_value.name, &c_unset_value); + + /* Create db for layer */ + c_create_db = (Command) { "create-db", "Create the database file for a layer", + 1, 1, "layer", &cli_create_db, STRING }; + hashmap_put(commands, c_create_db.name, &c_create_db); + + static struct option opts[] = { + { "config-file", 1, NULL, 'c' }, + { "direct", 0, NULL, 'd' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + while (true) { + c = getopt_long(argc, argv, "c:dvh", opts, &i); + + if (c == -1) { + break; + } + + switch (c) { + case 'c': + conf_path = strdup(optarg); + if (!conf_path) { + goto end; + } + break; + case 'd': + control.client.direct = true; + break; + case 'v': + version = true; + break; + case 'h': + help = true; + break; + } + } + + if (version) { + print_version(); + goto end; + } + + if (optind == argc) { + print_help(); + goto end; + } + + if ((command = hashmap_get(commands, argv[optind])) == NULL) { + printf("Unknown command: %s\n", argv[optind]); + goto end; + } + + /* We now execute the command */ + if (command->method == NULL) { + printf("Not yet implemented: %s\n", command->name); + goto end; + } + + if (help) { + /* Ensure we cleanup and abort when using help */ + print_usage(command); + ret = false; + goto end; + } + + if ((argc - optind - 1 < command->min_arguments) || + (argc - optind - 1 > command->max_arguments)) { + print_usage(command); + print_help(); + ret = false; + goto end; + } + + control.client.uid = geteuid(); + if (!control.client.direct) { + if (conf_path) { + if (buxton_set_conf_file(conf_path)) { + printf("Failed to set configuration file path\n"); + } + } + if (buxton_open(&client) < 0) { + control.client.direct = true; + } else { + control.client = *(_BuxtonClient *)client; + } + } + + if (control.client.direct) { + if (conf_path) { + int r; + struct stat st; + + r = stat(conf_path, &st); + if (r == -1) { + printf("Invalid configuration file path\n"); + goto end; + } else { + if (st.st_mode & S_IFDIR) { + printf("Configuration file given is a directory\n"); + goto end; + } + } + buxton_add_cmd_line(CONFIG_CONF_FILE, conf_path); + } + if (!buxton_direct_open(&(control))) { + printf("Failed to directly talk to Buxton\n"); + ret = false; + goto end; + } + } + + /* Connected to buxton_client, execute method */ + ret = command->method(&control, command->type, + optind + 1 < argc ? argv[optind + 1] : NULL, + optind + 2 < argc ? argv[optind + 2] : NULL, + optind + 3 < argc ? argv[optind + 3] : NULL, + optind + 4 < argc ? argv[optind + 4] : NULL); + +end: + free(conf_path); + hashmap_free(commands); + if (control.client.direct) { + buxton_direct_close(&control); + } else { + if (client) { + buxton_close(client); + } + } + + if (ret) { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/core/daemon.c b/src/core/daemon.c new file mode 100644 index 0000000..ee08b82 --- /dev/null +++ b/src/core/daemon.c @@ -0,0 +1,1134 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "direct.h" +#include "log.h" +#include "util.h" +#include "buxtonlist.h" + +bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list, + _BuxtonKey *key, BuxtonData **value) +{ + switch (msg) { + case BUXTON_CONTROL_SET: + if (count != 4) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING || list[3].type == BUXTON_TYPE_MIN || + list[3].type == BUXTON_TYPE_MAX) { + return false; + } + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + key->name = list[2].store.d_string; + key->type = list[3].type; + *value = &(list[3]); + break; + case BUXTON_CONTROL_SET_LABEL: + if (count == 3) { + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING) { + return false; + } + key->type = STRING; + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + *value = &list[2]; + } else if (count == 4) { + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING || list[3].type != STRING) { + return false; + } + key->type = STRING; + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + key->name = list[2].store.d_string; + *value = &list[3]; + } else { + return false; + } + break; + case BUXTON_CONTROL_CREATE_GROUP: + if (count != 2) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING) { + return false; + } + key->type = STRING; + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + break; + case BUXTON_CONTROL_REMOVE_GROUP: + if (count != 2) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING) { + return false; + } + key->type = STRING; + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + break; + case BUXTON_CONTROL_GET: + if (count == 4) { + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING || list[3].type != UINT32) { + return false; + } + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + key->name = list[2].store.d_string; + key->type = list[3].store.d_uint32; + } else if (count == 3) { + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != UINT32) { + return false; + } + key->group = list[0].store.d_string; + key->name = list[1].store.d_string; + key->type = list[2].store.d_uint32; + } else { + return false; + } + break; + case BUXTON_CONTROL_LIST: + return false; + if (count != 1) { + return false; + } + if (list[0].type != STRING) { + return false; + } + *value = &list[0]; + break; + case BUXTON_CONTROL_UNSET: + if (count != 4) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING || list[3].type != UINT32) { + return false; + } + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + key->name = list[2].store.d_string; + key->type = list[3].store.d_uint32; + break; + case BUXTON_CONTROL_NOTIFY: + if (count != 3) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != UINT32) { + return false; + } + key->group = list[0].store.d_string; + key->name = list[1].store.d_string; + key->type = list[2].store.d_uint32; + break; + case BUXTON_CONTROL_UNNOTIFY: + if (count != 3) { + return false; + } + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != UINT32) { + return false; + } + key->group = list[0].store.d_string; + key->name = list[1].store.d_string; + key->type = list[2].store.d_uint32; + break; + default: + return false; + } + + return true; +} + +bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t size) +{ + BuxtonControlMessage msg; + int32_t response; + BuxtonData *list = NULL; + _cleanup_buxton_data_ BuxtonData *data = NULL; + uint16_t i; + ssize_t p_count; + size_t response_len; + BuxtonData response_data, mdata; + BuxtonData *value = NULL; + _BuxtonKey key = {{0}, {0}, {0}, 0}; + BuxtonArray *out_list = NULL, *key_list = NULL; + _cleanup_free_ uint8_t *response_store = NULL; + uid_t uid; + bool ret = false; + uint32_t msgid = 0; + uint32_t n_msgid = 0; + + assert(self); + assert(client); + + uid = self->buxton.client.uid; + p_count = buxton_deserialize_message((uint8_t*)client->data, &msg, size, + &msgid, &list); + if (p_count < 0) { + if (errno == ENOMEM) { + abort(); + } + /* Todo: terminate the client due to invalid message */ + buxton_debug("Failed to deserialize message\n"); + goto end; + } + + /* Check valid range */ + if (msg <= BUXTON_CONTROL_MIN || msg >= BUXTON_CONTROL_MAX) { + goto end; + } + + if (!parse_list(msg, (size_t)p_count, list, &key, &value)) { + goto end; + } + + /* use internal function from buxtond */ + switch (msg) { + case BUXTON_CONTROL_SET: + set_value(self, client, &key, value, &response); + break; + case BUXTON_CONTROL_SET_LABEL: + set_label(self, client, &key, value, &response); + break; + case BUXTON_CONTROL_CREATE_GROUP: + create_group(self, client, &key, &response); + break; + case BUXTON_CONTROL_REMOVE_GROUP: + remove_group(self, client, &key, &response); + break; + case BUXTON_CONTROL_GET: + data = get_value(self, client, &key, &response); + break; + case BUXTON_CONTROL_UNSET: + unset_value(self, client, &key, &response); + break; + case BUXTON_CONTROL_LIST: + key_list = list_keys(self, client, &value->store.d_string, + &response); + break; + case BUXTON_CONTROL_NOTIFY: + register_notification(self, client, &key, msgid, &response); + break; + case BUXTON_CONTROL_UNNOTIFY: + n_msgid = unregister_notification(self, client, &key, &response); + break; + default: + goto end; + } + /* Set a response code */ + response_data.type = INT32; + response_data.store.d_int32 = response; + out_list = buxton_array_new(); + if (!out_list) { + abort(); + } + if (!buxton_array_add(out_list, &response_data)) { + abort(); + } + + + switch (msg) { + /* TODO: Use cascading switch */ + case BUXTON_CONTROL_SET: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize set response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_SET_LABEL: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize set_label response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_CREATE_GROUP: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize create_group response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_REMOVE_GROUP: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize remove_group response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_GET: + if (data && !buxton_array_add(out_list, data)) { + abort(); + } + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize get response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_UNSET: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize unset response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_LIST: + if (key_list) { + for (i = 0; i < key_list->len; i++) { + if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) { + abort(); + } + } + buxton_array_free(&key_list, NULL); + } + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize list response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_NOTIFY: + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize notify response message\n"); + abort(); + } + break; + case BUXTON_CONTROL_UNNOTIFY: + mdata.type = UINT32; + mdata.store.d_uint32 = n_msgid; + if (!buxton_array_add(out_list, &mdata)) { + abort(); + } + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize unnotify response message\n"); + abort(); + } + break; + default: + goto end; + } + + /* Now write the response */ + ret = _write(client->fd, response_store, response_len); + if (ret) { + if (msg == BUXTON_CONTROL_SET && response == 0) { + buxtond_notify_clients(self, client, &key, value); + } else if (msg == BUXTON_CONTROL_UNSET && response == 0) { + buxtond_notify_clients(self, client, &key, NULL); + } + } + +end: + /* Restore our own UID */ + self->buxton.client.uid = uid; + if (out_list) { + buxton_array_free(&out_list, NULL); + } + if (list) { + for (i=0; i < p_count; i++) { + if (list[i].type == STRING) { + free(list[i].store.d_string.value); + } + } + free(list); + } + return ret; +} + +void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, BuxtonData *value) +{ + BuxtonList *list = NULL; + BuxtonList *elem = NULL; + BuxtonNotification *nitem; + _cleanup_free_ uint8_t* response = NULL; + size_t response_len; + BuxtonArray *out_list = NULL; + _cleanup_free_ char *key_name; + int r; + + assert(self); + assert(client); + assert(key); + + r = asprintf(&key_name, "%s%s", key->group.value, key->name.value); + if (r == -1) { + abort(); + } + list = hashmap_get(self->notify_mapping, key_name); + if (!list) { + return; + } + + BUXTON_LIST_FOREACH(list, elem) { + nitem = elem->data; + int c = 1; + __attribute__((unused)) bool unused; + free(response); + response = NULL; + + if (nitem->old_data && value) { + switch (value->type) { + case STRING: + c = memcmp((const void *) + (nitem->old_data->store.d_string.value), + (const void *)(value->store.d_string.value), + value->store.d_string.length); + break; + case INT32: + c = memcmp((const void *)&(nitem->old_data->store.d_int32), + (const void *)&(value->store.d_int32), + sizeof(int32_t)); + break; + case UINT32: + c = memcmp((const void *)&(nitem->old_data->store.d_uint32), + (const void *)&(value->store.d_uint32), + sizeof(uint32_t)); + break; + case INT64: + c = memcmp((const void *)&(nitem->old_data->store.d_int64), + (const void *)&(value->store.d_int64), + sizeof(int64_t)); + break; + case UINT64: + c = memcmp((const void *)&(nitem->old_data->store.d_uint64), + (const void *)&(value->store.d_uint64), + sizeof(uint64_t)); + break; + case FLOAT: + c = memcmp((const void *)&(nitem->old_data->store.d_float), + (const void *)&(value->store.d_float), + sizeof(float)); + break; + case DOUBLE: + c = memcmp((const void *)&(nitem->old_data->store.d_double), + (const void *)&(value->store.d_double), + sizeof(double)); + break; + case BOOLEAN: + c = memcmp((const void *)&(nitem->old_data->store.d_boolean), + (const void *)&(value->store.d_boolean), + sizeof(bool)); + break; + default: + buxton_log("Internal state corruption: Notification data type invalid\n"); + abort(); + } + } + + if (!c) { + continue; + } + if (nitem->old_data && (nitem->old_data->type == STRING)) { + free(nitem->old_data->store.d_string.value); + } + free(nitem->old_data); + + nitem->old_data = malloc0(sizeof(BuxtonData)); + if (!nitem->old_data) { + abort(); + } + if (value) { + if (!buxton_data_copy(value, nitem->old_data)) { + abort(); + } + } + + out_list = buxton_array_new(); + if (!out_list) { + abort(); + } + if (value) { + if (!buxton_array_add(out_list, value)) { + abort(); + } + } + + response_len = buxton_serialize_message(&response, + BUXTON_CONTROL_CHANGED, + nitem->msgid, out_list); + buxton_array_free(&out_list, NULL); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize notification\n"); + abort(); + } + buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd, + key_name); + + unused = _write(nitem->client->fd, response, response_len); + } +} + +void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, + BuxtonData *value, int32_t *status) +{ + + assert(self); + assert(client); + assert(key); + assert(value); + assert(status); + + *status = -1; + + buxton_debug("Daemon setting [%s][%s][%s]\n", + key->layer.value, + key->group.value, + key->name.value); + + self->buxton.client.uid = client->cred.uid; + + if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) { + return; + } + + *status = 0; + buxton_debug("Daemon set value completed\n"); +} + +void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, + BuxtonData *value, int32_t *status) +{ + + assert(self); + assert(client); + assert(key); + assert(value); + assert(status); + + *status = -1; + + buxton_debug("Daemon setting label on [%s][%s][%s]\n", + key->layer.value, + key->group.value, + key->name.value); + + self->buxton.client.uid = client->cred.uid; + + /* Use internal library to set label */ + if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) { + return; + } + + *status = 0; + buxton_debug("Daemon set label completed\n"); +} + +void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, + int32_t *status) +{ + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + buxton_debug("Daemon creating group [%s][%s]\n", + key->layer.value, + key->group.value); + + self->buxton.client.uid = client->cred.uid; + + /* Use internal library to create group */ + if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) { + return; + } + + *status = 0; + buxton_debug("Daemon create group completed\n"); +} + +void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, + int32_t *status) +{ + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + buxton_debug("Daemon removing group [%s][%s]\n", + key->layer.value, + key->group.value); + + self->buxton.client.uid = client->cred.uid; + + /* Use internal library to create group */ + if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) { + return; + } + + *status = 0; + buxton_debug("Daemon remove group completed\n"); +} + +void unset_value(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, int32_t *status) +{ + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + buxton_debug("Daemon unsetting [%s][%s][%s]\n", + key->layer.value, + key->group.value, + key->name.value); + + /* Use internal library to unset value */ + self->buxton.client.uid = client->cred.uid; + if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) { + return; + } + + buxton_debug("unset value returned successfully from db\n"); + + *status = 0; + buxton_debug("Daemon unset value completed\n"); +} + +BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, int32_t *status) +{ + BuxtonData *data = NULL; + BuxtonString label; + int32_t ret; + + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + data = malloc0(sizeof(BuxtonData)); + if (!data) { + abort(); + } + + if (key->layer.value) { + buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value, + key->group.value, key->name.value); + } else { + buxton_debug("Daemon getting [%s][%s]\n", key->group.value, + key->name.value); + } + self->buxton.client.uid = client->cred.uid; + ret = buxton_direct_get_value(&self->buxton, key, data, &label, + client->smack_label); + if (ret) { + goto fail; + } + + free(label.value); + buxton_debug("get value returned successfully from db\n"); + + *status = 0; + goto end; +fail: + buxton_debug("get value failed\n"); + free(data); + data = NULL; +end: + + return data; +} + +BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client, + BuxtonString *layer, int32_t *status) +{ + BuxtonArray *ret_list = NULL; + assert(self); + assert(client); + assert(layer); + assert(status); + + *status = -1; + if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) { + *status = 0; + } + return ret_list; +} + +void register_notification(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, uint32_t msgid, + int32_t *status) +{ + BuxtonList *n_list = NULL; + BuxtonNotification *nitem; + BuxtonData *old_data = NULL; + int32_t key_status; + char *key_name; + int r; + + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + nitem = malloc0(sizeof(BuxtonNotification)); + if (!nitem) { + abort(); + } + nitem->client = client; + + /* Store data now, cheap */ + old_data = get_value(self, client, key, &key_status); + if (key_status != 0) { + free(nitem); + return; + } + nitem->old_data = old_data; + nitem->msgid = msgid; + + /* May be null, but will append regardless */ + r = asprintf(&key_name, "%s%s", key->group.value, key->name.value); + if (r == -1) { + abort(); + } + n_list = hashmap_get(self->notify_mapping, key_name); + + if (!n_list) { + if (!buxton_list_append(&n_list, nitem)) { + abort(); + } + + if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) { + abort(); + } + } else { + free(key_name); + if (!buxton_list_append(&n_list, nitem)) { + abort(); + } + } + *status = 0; +} + +uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, int32_t *status) +{ + BuxtonList *n_list = NULL; + BuxtonList *elem = NULL; + BuxtonNotification *nitem, *citem = NULL; + uint32_t msgid = 0; + _cleanup_free_ char *key_name = NULL; + void *old_key_name; + int r; + + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + r = asprintf(&key_name, "%s%s", key->group.value, key->name.value); + if (r == -1) { + abort(); + } + n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name); + /* This key isn't actually registered for notifications */ + if (!n_list) { + return 0; + } + + BUXTON_LIST_FOREACH(n_list, elem) { + nitem = elem->data; + /* Find the list item for this client */ + if (nitem->client == client) { + citem = nitem; + } + break; + }; + + /* Client hasn't registered for notifications on this key */ + if (!citem) { + return 0; + } + + msgid = citem->msgid; + /* Remove client from notifications */ + free_buxton_data(&(citem->old_data)); + buxton_list_remove(&n_list, citem, true); + + /* If we removed the last item, remove the mapping too */ + if (!n_list) { + (void)hashmap_remove(self->notify_mapping, key_name); + free(old_key_name); + } + + *status = 0; + + return msgid; +} + +bool identify_client(client_list_item *cl) +{ + /* Identity handling */ + ssize_t nr; + int data; + struct msghdr msgh; + struct iovec iov; + __attribute__((unused)) struct ucred *ucredp; + struct cmsghdr *cmhp; + socklen_t len = sizeof(struct ucred); + int on = 1; + + assert(cl); + + union { + struct cmsghdr cmh; + char control[CMSG_SPACE(sizeof(struct ucred))]; + } control_un; + + setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + control_un.cmh.cmsg_level = SOL_SOCKET; + control_un.cmh.cmsg_type = SCM_CREDENTIALS; + + msgh.msg_control = control_un.control; + msgh.msg_controllen = sizeof(control_un.control); + + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + iov.iov_base = &data; + iov.iov_len = sizeof(int); + + msgh.msg_name = NULL; + msgh.msg_namelen = 0; + + nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT); + if (nr == -1) { + return false; + } + + cmhp = CMSG_FIRSTHDR(&msgh); + + if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + buxton_log("Invalid cmessage header from kernel\n"); + abort(); + } + + if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) { + buxton_log("Missing credentials on socket\n"); + abort(); + } + + ucredp = (struct ucred *) CMSG_DATA(cmhp); + + if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) { + buxton_log("Missing label on socket\n"); + abort(); + } + + return true; +} + +void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a) +{ + assert(self); + assert(fd >= 0); + + if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc), + (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) { + abort(); + } + if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc), + (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) { + abort(); + } + self->pollfds[self->nfds].fd = fd; + self->pollfds[self->nfds].events = events; + self->pollfds[self->nfds].revents = 0; + self->accepting[self->nfds] = a; + self->nfds++; + + buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a); +} + +void del_pollfd(BuxtonDaemon *self, nfds_t i) +{ + assert(self); + assert(i < self->nfds); + + buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd); + + if (i != (self->nfds - 1)) { + memmove(&(self->pollfds[i]), + &(self->pollfds[i + 1]), + /* + * nfds < max int because of kernel limit of + * fds. i + 1 < nfds because of if and assert + * so the casts below are always >= 0 and less + * than long unsigned max int so no loss of + * precision. + */ + (size_t)(self->nfds - i - 1) * sizeof(struct pollfd)); + memmove(&(self->accepting[i]), + &(self->accepting[i + 1]), + (size_t)(self->nfds - i - 1) * sizeof(bool)); + } + self->nfds--; +} + +void handle_smack_label(client_list_item *cl) +{ + socklen_t slabel_len = 1; + char *buf = NULL; + BuxtonString *slabel = NULL; + int ret; + + ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len); + /* libsmack ignores ERANGE here, so we ignore it too */ + if (ret < 0 && errno != ERANGE) { + switch (errno) { + case ENOPROTOOPT: + /* If Smack is not enabled, do not set the client label */ + cl->smack_label = NULL; + return; + default: + buxton_log("getsockopt(): %m\n"); + exit(EXIT_FAILURE); + } + } + + slabel = malloc0(sizeof(BuxtonString)); + if (!slabel) { + abort(); + } + + /* already checked slabel_len positive above */ + buf = malloc0((size_t)slabel_len + 1); + if (!buf) { + abort(); + } + + ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len); + if (ret < 0) { + buxton_log("getsockopt(): %m\n"); + exit(EXIT_FAILURE); + } + + slabel->value = buf; + slabel->length = (uint32_t)slabel_len; + + buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value); + + cl->smack_label = slabel; +} + +bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i) +{ + ssize_t l; + uint16_t peek; + bool more_data = false; + int message_limit = 32; + + assert(self); + assert(cl); + + if (!cl->data) { + cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH); + cl->offset = 0; + cl->size = BUXTON_MESSAGE_HEADER_LENGTH; + } + if (!cl->data) { + abort(); + } + /* client closed the connection, or some error occurred? */ + if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) { + goto terminate; + } + + /* need to authenticate the client? */ + if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) { + if (!identify_client(cl)) { + goto terminate; + } + + handle_smack_label(cl); + } + + buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid); + + /* Hand off any read data */ + do { + l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset); + + /* + * Close clients with read errors. If there isn't more + * data and we don't have a complete message just + * cleanup and let the client resend their request. + */ + if (l < 0) { + if (errno != EAGAIN) { + goto terminate; + } else { + goto cleanup; + } + } else if (l == 0) { + goto cleanup; + } + + cl->offset += (size_t)l; + if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) { + continue; + } + if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) { + cl->size = buxton_get_message_size(cl->data, cl->offset); + if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) { + goto terminate; + } + } + if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) { + cl->data = realloc(cl->data, cl->size); + if (!cl->data) { + abort(); + } + } + if (cl->size != cl->offset) { + continue; + } + if (!buxtond_handle_message(self, cl, cl->size)) { + buxton_log("Communication failed with client %d\n", cl->fd); + goto terminate; + } + + message_limit--; + if (message_limit) { + cl->size = BUXTON_MESSAGE_HEADER_LENGTH; + cl->offset = 0; + continue; + } + if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) { + more_data = true; + } + goto cleanup; + } while (l > 0); + +cleanup: + free(cl->data); + cl->data = NULL; + cl->size = BUXTON_MESSAGE_HEADER_LENGTH; + cl->offset = 0; + return more_data; + +terminate: + terminate_client(self, cl, i); + return more_data; +} + +void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i) +{ + del_pollfd(self, i); + close(cl->fd); + if (cl->smack_label) { + free(cl->smack_label->value); + } + free(cl->smack_label); + free(cl->data); + buxton_debug("Closed connection from fd %d\n", cl->fd); + LIST_REMOVE(client_list_item, item, self->client_list, cl); + free(cl); + cl = NULL; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/core/daemon.h b/src/core/daemon.h new file mode 100644 index 0000000..7c5f5c0 --- /dev/null +++ b/src/core/daemon.h @@ -0,0 +1,266 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file daemon.h Internal header + * This file is used internally by buxton to provide functionality + * used for the buxtond + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +#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); /** +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "backend.h" +#include "daemon.h" +#include "direct.h" +#include "list.h" +#include "log.h" +#include "smack.h" +#include "util.h" +#include "configurator.h" +#include "buxtonlist.h" + +#define SOCKET_TIMEOUT 5 + +static BuxtonDaemon self; + +static void print_usage(char *name) +{ + printf("%s: Usage\n\n", name); + + printf(" -c, --config-file Path to configuration file\n"); + printf(" -h, --help Display this help message\n"); +} + +/** + * Entry point into buxtond + * @param argc Number of arguments passed + * @param argv An array of string arguments + * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE + */ +int main(int argc, char *argv[]) +{ + int fd; + int smackfd = -1; + socklen_t addr_len; + struct sockaddr_un remote; + int descriptors; + int ret; + bool manual_start = false; + sigset_t mask; + int sigfd; + bool leftover_messages = false; + struct stat st; + bool help = false; + BuxtonList *map_list = NULL; + Iterator iter; + char *notify_key; + + static struct option opts[] = { + { "config-file", 1, NULL, 'c' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + while (true) { + int c; + int i; + c = getopt_long(argc, argv, "c:h", opts, &i); + + if (c == -1) { + break; + } + + switch (c) { + case 'c': + ret = stat(optarg, &st); + if (ret == -1) { + buxton_log("Invalid configuration file path\n"); + exit(EXIT_FAILURE); + } else { + if (st.st_mode & S_IFDIR) { + buxton_log("Configuration file given is a directory\n"); + exit(EXIT_FAILURE); + } + } + + buxton_add_cmd_line(CONFIG_CONF_FILE, optarg); + break; + case 'h': + help = true; + break; + } + } + + if (help) { + print_usage(argv[0]); + exit(EXIT_SUCCESS); + } + + if (!buxton_cache_smack_rules()) { + exit(EXIT_FAILURE); + } + smackfd = buxton_watch_smack_rules(); + if (smackfd < 0 && errno) { + exit(EXIT_FAILURE); + } + + self.nfds_alloc = 0; + self.accepting_alloc = 0; + self.nfds = 0; + self.buxton.client.direct = true; + self.buxton.client.uid = geteuid(); + if (!buxton_direct_open(&self.buxton)) { + exit(EXIT_FAILURE); + } + + sigemptyset(&mask); + ret = sigaddset(&mask, SIGINT); + if (ret != 0) { + exit(EXIT_FAILURE); + } + ret = sigaddset(&mask, SIGTERM); + if (ret != 0) { + exit(EXIT_FAILURE); + } + ret = sigaddset(&mask, SIGPIPE); + if (ret != 0) { + exit(EXIT_FAILURE); + } + + ret = sigprocmask(SIG_BLOCK, &mask, NULL); + if (ret == -1) { + exit(EXIT_FAILURE); + } + + sigfd = signalfd(-1, &mask, 0); + if (sigfd == -1) { + exit(EXIT_FAILURE); + } + + add_pollfd(&self, sigfd, POLLIN, false); + + /* For client notifications */ + self.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + /* Store a list of connected clients */ + LIST_HEAD_INIT(client_list_item, self.client_list); + + descriptors = sd_listen_fds(0); + if (descriptors < 0) { + buxton_log("sd_listen_fds: %m\n"); + exit(EXIT_FAILURE); + } else if (descriptors == 0) { + /* Manual invocation */ + manual_start = true; + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + buxton_log("socket(): %m\n"); + exit(EXIT_FAILURE); + } + + memzero(&sa, sizeof(sa)); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, buxton_socket(), sizeof(sa.un.sun_path) - 1); + sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; + + ret = unlink(sa.un.sun_path); + if (ret == -1 && errno != ENOENT) { + exit(EXIT_FAILURE); + } + + if (bind(fd, &sa.sa, sizeof(sa)) < 0) { + buxton_log("bind(): %m\n"); + exit(EXIT_FAILURE); + } + + chmod(sa.un.sun_path, 0666); + + if (listen(fd, SOMAXCONN) < 0) { + buxton_log("listen(): %m\n"); + exit(EXIT_FAILURE); + } + add_pollfd(&self, fd, POLLIN | POLLPRI, true); + } else { + /* systemd socket activation */ + for (fd = SD_LISTEN_FDS_START + 0; fd < SD_LISTEN_FDS_START + descriptors; fd++) { + if (sd_is_fifo(fd, NULL)) { + add_pollfd(&self, fd, POLLIN, false); + buxton_debug("Added fd %d type FIFO\n", fd); + } else if (sd_is_socket_unix(fd, SOCK_STREAM, -1, buxton_socket(), 0)) { + add_pollfd(&self, fd, POLLIN | POLLPRI, true); + buxton_debug("Added fd %d type UNIX\n", fd); + } else if (sd_is_socket(fd, AF_UNSPEC, 0, -1)) { + add_pollfd(&self, fd, POLLIN | POLLPRI, true); + buxton_debug("Added fd %d type SOCKET\n", fd); + } + } + } + + if (smackfd >= 0) { + /* add Smack rule fd to pollfds */ + add_pollfd(&self, smackfd, POLLIN | POLLPRI, false); + } + + buxton_log("%s: Started\n", argv[0]); + + /* Enter loop to accept clients */ + for (;;) { + ret = poll(self.pollfds, self.nfds, leftover_messages ? 0 : -1); + + if (ret < 0) { + buxton_log("poll(): %m\n"); + break; + } + if (ret == 0) { + if (!leftover_messages) { + continue; + } + } + + leftover_messages = false; + + /* check sigfd if the daemon was signaled */ + if (self.pollfds[0].revents != 0) { + ssize_t sinfo; + struct signalfd_siginfo si; + + sinfo = read(self.pollfds[0].fd, &si, sizeof(struct signalfd_siginfo)); + if (sinfo != sizeof(struct signalfd_siginfo)) { + exit(EXIT_FAILURE); + } + + if (si.ssi_signo == SIGINT || si.ssi_signo == SIGTERM) { + break; + } + } + + for (nfds_t i = 1; i < self.nfds; i++) { + client_list_item *cl = NULL; + char discard[256]; + + if (self.pollfds[i].revents == 0) { + continue; + } + + if (self.pollfds[i].fd == -1) { + /* TODO: Remove client from list */ + buxton_debug("Removing / Closing client for fd %d\n", self.pollfds[i].fd); + del_pollfd(&self, i); + continue; + } + + if (smackfd >= 0) { + if (self.pollfds[i].fd == smackfd) { + if (!buxton_cache_smack_rules()) { + exit(EXIT_FAILURE); + } + buxton_log("Reloaded Smack access rules\n"); + /* discard inotify data itself */ + while (read(smackfd, &discard, 256) == 256); + continue; + } + } + + if (self.accepting[i] == true) { + struct timeval tv; + int fd; + int on = 1; + + addr_len = sizeof(remote); + + if ((fd = accept(self.pollfds[i].fd, + (struct sockaddr *)&remote, &addr_len)) == -1) { + buxton_log("accept(): %m\n"); + break; + } + + buxton_debug("New client fd %d connected through fd %d\n", fd, self.pollfds[i].fd); + + if (fcntl(fd, F_SETFL, O_NONBLOCK)) { + close(fd); + break; + } + + cl = malloc0(sizeof(client_list_item)); + if (!cl) { + exit(EXIT_FAILURE); + } + + LIST_INIT(client_list_item, item, cl); + + cl->fd = fd; + cl->cred = (struct ucred) {0, 0, 0}; + LIST_PREPEND(client_list_item, item, self.client_list, cl); + + /* poll for data on this new client as well */ + add_pollfd(&self, cl->fd, POLLIN | POLLPRI, false); + + /* Mark our packets as high prio */ + if (setsockopt(cl->fd, SOL_SOCKET, SO_PRIORITY, &on, sizeof(on)) == -1) { + buxton_log("setsockopt(SO_PRIORITY): %m\n"); + } + + /* Set socket recv timeout */ + tv.tv_sec = SOCKET_TIMEOUT; + tv.tv_usec = 0; + if (setsockopt(cl->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, + sizeof(struct timeval)) == -1) { + buxton_log("setsockopt(SO_RCVTIMEO): %m\n"); + } + + /* check if this is optimal or not */ + break; + } + + assert(self.accepting[i] == 0); + if (smackfd >= 0) { + assert(self.pollfds[i].fd != smackfd); + } + + /* handle data on any connection */ + /* TODO: Replace with hash table lookup */ + LIST_FOREACH(item, cl, self.client_list) + if (self.pollfds[i].fd == cl->fd) { + break; + } + + assert(cl); + if (handle_client(&self, cl, i)) { + leftover_messages = true; + } + } + } + + buxton_log("%s: Closing all connections\n", argv[0]); + + if (manual_start) { + unlink(buxton_socket()); + } + for (int i = 0; i < self.nfds; i++) { + close(self.pollfds[i].fd); + } + for (client_list_item *i = self.client_list; i;) { + client_list_item *j = i->item_next; + free(i); + i = j; + } + /* Clean up notification lists */ + HASHMAP_FOREACH_KEY(map_list, notify_key, self.notify_mapping, iter) { + hashmap_remove(self.notify_mapping, notify_key); + BuxtonList *elem; + BUXTON_LIST_FOREACH(map_list, elem) { + BuxtonNotification *notif = (BuxtonNotification*)elem->data; + if (notif->old_data) { + free_buxton_data(&(notif->old_data)); + } + } + free(notify_key); + buxton_list_free_all(&map_list); + } + + hashmap_free(self.notify_mapping); + buxton_direct_close(&self.buxton); + return EXIT_SUCCESS; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/db/gdbm.c b/src/db/gdbm.c new file mode 100644 index 0000000..e8988c4 --- /dev/null +++ b/src/db/gdbm.c @@ -0,0 +1,452 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "log.h" +#include "hashmap.h" +#include "serialize.h" +#include "util.h" + +/** + * GDBM Database Module + */ + + +static Hashmap *_resources = NULL; + +static char *key_get_name(BuxtonString *key) +{ + char *c; + + c = strchr(key->value, 0); + if (!c) { + return NULL; + } + if (c - (key->value + (key->length - 1)) >= 0) { + return NULL; + } + c++; + + return c; +} + +static GDBM_FILE try_open_database(char *path, const int oflag) +{ + GDBM_FILE db = gdbm_open(path, 0, oflag, S_IRUSR | S_IWUSR, NULL); + /* handle open under write mode failing by falling back to + reader mode */ + if (!db && (gdbm_errno == GDBM_FILE_OPEN_ERROR)) { + db = gdbm_open(path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL); + buxton_debug("Attempting to fallback to opening db as read-only\n"); + errno = EROFS; + } else { + if (!db) { + abort(); + } + /* Must do this as gdbm_open messes with errno */ + errno = 0; + } + return db; +} + +/* Open or create databases on the fly */ +static GDBM_FILE db_for_resource(BuxtonLayer *layer) +{ + GDBM_FILE db; + _cleanup_free_ char *path = NULL; + char *name = NULL; + int r; + int oflag = layer->readonly ? GDBM_READER : GDBM_WRCREAT; + int save_errno = 0; + + assert(layer); + assert(_resources); + + if (layer->type == LAYER_USER) { + r = asprintf(&name, "%s-%d", layer->name.value, layer->uid); + } else { + r = asprintf(&name, "%s", layer->name.value); + } + if (r == -1) { + abort(); + } + + db = hashmap_get(_resources, name); + if (!db) { + path = get_layer_path(layer); + if (!path) { + abort(); + } + + db = try_open_database(path, oflag); + save_errno = errno; + if (!db) { + free(name); + buxton_log("Couldn't create db for path: %s\n", path); + return 0; + } + r = hashmap_put(_resources, name, db); + if (r != 1) { + abort(); + } + } else { + db = (GDBM_FILE) hashmap_get(_resources, name); + free(name); + } + + errno = save_errno; + return db; +} + +static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + GDBM_FILE db; + int ret = -1; + datum key_data; + datum cvalue = {0}; + datum value; + _cleanup_free_ uint8_t *data_store = NULL; + size_t size; + uint32_t sz; + BuxtonData cdata = {0}; + BuxtonString clabel; + + assert(layer); + assert(key); + assert(label); + + if (key->name.value) { + sz = key->group.length + key->name.length; + key_data.dptr = malloc(sz); + if (!key_data.dptr) { + abort(); + } + + /* size is string\0string\0 so just write, bonus for + nil seperator being added without extra work */ + key_data.dsize = (int)sz; + memcpy(key_data.dptr, key->group.value, key->group.length); + memcpy(key_data.dptr + key->group.length, key->name.value, + key->name.length); + } else { + key_data.dptr = malloc(key->group.length); + if (!key_data.dptr) { + abort(); + } + + memcpy(key_data.dptr, key->group.value, key->group.length); + key_data.dsize = (int)key->group.length; + } + + db = db_for_resource(layer); + if (!db || errno) { + ret = errno; + goto end; + } + + /* set_label will pass a NULL for data */ + if (!data) { + cvalue = gdbm_fetch(db, key_data); + if (cvalue.dsize < 0 || cvalue.dptr == NULL) { + ret = ENOENT; + goto end; + } + + data_store = (uint8_t*)cvalue.dptr; + buxton_deserialize(data_store, &cdata, &clabel); + free(clabel.value); + data = &cdata; + data_store = NULL; + } + + size = buxton_serialize(data, label, &data_store); + + value.dptr = (char *)data_store; + value.dsize = (int)size; + ret = gdbm_store(db, key_data, value, GDBM_REPLACE); + if (ret && gdbm_errno == GDBM_READER_CANT_STORE) { + ret = EROFS; + } + assert(ret == 0); + +end: + if (cdata.type == STRING) { + free(cdata.store.d_string.value); + } + free(key_data.dptr); + free(cvalue.dptr); + + return ret; +} + +static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + GDBM_FILE db; + datum key_data; + datum value; + uint8_t *data_store = NULL; + int ret; + uint32_t sz; + + assert(layer); + + if (key->name.value) { + sz = key->group.length + key->name.length; + key_data.dptr = malloc(sz); + if (!key_data.dptr) { + abort(); + } + + /* size is string\0string\0 so just write, bonus for + nil seperator being added without extra work */ + key_data.dsize = (int)sz; + memcpy(key_data.dptr, key->group.value, key->group.length); + memcpy(key_data.dptr + key->group.length, key->name.value, + key->name.length); + } else { + key_data.dptr = malloc(key->group.length); + if (!key_data.dptr) { + abort(); + } + + memcpy(key_data.dptr, key->group.value, key->group.length); + key_data.dsize = (int)key->group.length; + } + + memzero(&value, sizeof(datum)); + db = db_for_resource(layer); + if (!db) { + /* + * Set negative here to indicate layer not found + * rather than key not found, optimization for + * set value + */ + ret = -ENOENT; + goto end; + } + + value = gdbm_fetch(db, key_data); + if (value.dsize < 0 || value.dptr == NULL) { + ret = ENOENT; + goto end; + } + + data_store = (uint8_t*)value.dptr; + buxton_deserialize(data_store, data, label); + + if (data->type != key->type) { + free(label->value); + label->value = NULL; + if (data->type == STRING) { + free(data->store.d_string.value); + data->store.d_string.value = NULL; + } + ret = EINVAL; + goto end; + } + ret = 0; + +end: + free(key_data.dptr); + free(value.dptr); + data_store = NULL; + + return ret; +} + +static int unset_value(BuxtonLayer *layer, + _BuxtonKey *key, + __attribute__((unused)) BuxtonData *data, + __attribute__((unused)) BuxtonString *label) +{ + GDBM_FILE db; + datum key_data; + int ret; + uint32_t sz; + + assert(layer); + assert(key); + + if (key->name.value) { + sz = key->group.length + key->name.length; + key_data.dptr = malloc(sz); + if (!key_data.dptr) { + abort(); + } + + /* size is string\0string\0 so just write, bonus for + nil seperator being added without extra work */ + key_data.dsize = (int)sz; + memcpy(key_data.dptr, key->group.value, key->group.length); + memcpy(key_data.dptr + key->group.length, key->name.value, + key->name.length); + } else { + key_data.dptr = malloc(key->group.length); + if (!key_data.dptr) { + abort(); + } + + memcpy(key_data.dptr, key->group.value, key->group.length); + key_data.dsize = (int)key->group.length; + } + + errno = 0; + db = db_for_resource(layer); + if (!db || gdbm_errno) { + ret = EROFS; + goto end; + } + + ret = gdbm_delete(db, key_data); + if (ret) { + if (gdbm_errno == GDBM_READER_CANT_DELETE) { + ret = EROFS; + } else if (gdbm_errno == GDBM_ITEM_NOT_FOUND) { + ret = ENOENT; + } else { + abort(); + } + } + +end: + free(key_data.dptr); + + return ret; +} + +static bool list_keys(BuxtonLayer *layer, + BuxtonArray **list) +{ + GDBM_FILE db; + datum key, nextkey; + BuxtonArray *k_list = NULL; + BuxtonData *current = NULL; + BuxtonString in_key; + char *name; + bool ret = false; + + assert(layer); + + db = db_for_resource(layer); + if (!db) { + goto end; + } + + k_list = buxton_array_new(); + key = gdbm_firstkey(db); + /* Iterate through all of the keys */ + while (key.dptr) { + /* Split the key name from the rest of the key */ + in_key.value = (char*)key.dptr; + in_key.length = (uint32_t)key.dsize; + name = key_get_name(&in_key); + if (!name) { + continue; + } + + current = malloc0(sizeof(BuxtonData)); + if (!current) { + abort(); + } + current->type = STRING; + current->store.d_string.value = strdup(name); + if (!current->store.d_string.value) { + abort(); + } + current->store.d_string.length = (uint32_t)strlen(name) + 1; + if (!buxton_array_add(k_list, current)) { + abort(); + } + + /* Visit the next key */ + nextkey = gdbm_nextkey(db, key); + free(key.dptr); + key = nextkey; + } + + /* Pass ownership of the array to the caller */ + *list = k_list; + ret = true; + +end: + if (!ret && k_list) { + for (uint16_t i = 0; i < k_list->len; i++) { + current = buxton_array_get(k_list, i); + if (!current) { + break; + } + free(current->store.d_string.value); + free(current); + } + buxton_array_free(&k_list, NULL); + } + return ret; +} + +_bx_export_ void buxton_module_destroy(void) +{ + const char *key; + Iterator iterator; + GDBM_FILE db; + + /* close all gdbm handles */ + HASHMAP_FOREACH_KEY(db, key, _resources, iterator) { + hashmap_remove(_resources, key); + gdbm_close(db); + free((void *)key); + } + hashmap_free(_resources); + _resources = NULL; +} + +_bx_export_ bool buxton_module_init(BuxtonBackend *backend) +{ + + assert(backend); + + /* Point the struct methods back to our own */ + backend->set_value = &set_value; + backend->get_value = &get_value; + backend->list_keys = &list_keys; + backend->unset_value = &unset_value; + backend->create_db = (module_db_init_func) &db_for_resource; + + _resources = hashmap_new(string_hash_func, string_compare_func); + if (!_resources) { + abort(); + } + + return true; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/db/memory.c b/src/db/memory.c new file mode 100644 index 0000000..d763390 --- /dev/null +++ b/src/db/memory.c @@ -0,0 +1,347 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +#include "hashmap.h" +#include "log.h" +#include "buxton.h" +#include "backend.h" +#include "util.h" + +/** + * Memory Database Module + * + * Used for quick testing and debugging of Buxton, to ensure protocol + * and direct access are working as intended. + * Note this is not persistent. + */ + + +static Hashmap *_resources; + +/* Return existing hashmap or create new hashmap on the fly */ +static Hashmap *_db_for_resource(BuxtonLayer *layer) +{ + Hashmap *db; + char *name = NULL; + int r; + + assert(layer); + assert(_resources); + + if (layer->type == LAYER_USER) { + r = asprintf(&name, "%s-%d", layer->name.value, layer->uid); + } else { + r = asprintf(&name, "%s", layer->name.value); + } + if (r == -1) { + return NULL; + } + + db = hashmap_get(_resources, name); + if (!db) { + db = hashmap_new(string_hash_func, string_compare_func); + hashmap_put(_resources, name, db); + } else { + free(name); + } + + return db; +} + +static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + Hashmap *db; + BuxtonArray *array = NULL; + BuxtonArray *stored; + BuxtonData *data_copy = NULL; + BuxtonData *d; + BuxtonString *label_copy = NULL; + BuxtonString *l; + char *full_key = NULL; + char *k; + int ret; + + assert(layer); + assert(key); + assert(label); + + db = _db_for_resource(layer); + if (!db) { + ret = ENOENT; + goto end; + } + + if (key->name.value) { + if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) { + abort(); + } + } else { + full_key = strdup(key->group.value); + if (!full_key) { + abort(); + } + } + + if (!data) { + stored = (BuxtonArray *)hashmap_get(db, full_key); + if (!stored) { + ret = ENOENT; + free(full_key); + goto end; + } + data = buxton_array_get(stored, 0); + } + + array = buxton_array_new(); + if (!array) { + abort(); + } + data_copy = malloc0(sizeof(BuxtonData)); + if (!data_copy) { + abort(); + } + label_copy = malloc0(sizeof(BuxtonString)); + if (!label_copy) { + abort(); + } + + if (!buxton_data_copy(data, data_copy)) { + abort(); + } + if (!buxton_string_copy(label, label_copy)) { + abort(); + } + if (!buxton_array_add(array, data_copy)) { + abort(); + } + if (!buxton_array_add(array, label_copy)) { + abort(); + } + if (!buxton_array_add(array, full_key)) { + abort(); + } + + ret = hashmap_put(db, full_key, array); + if (ret != 1) { + if (ret == -ENOMEM) { + abort(); + } + /* remove the old value */ + stored = (BuxtonArray *)hashmap_remove(db, full_key); + assert(stored); + + /* free the data */ + d = buxton_array_get(stored, 0); + data_free(d); + l = buxton_array_get(stored, 1); + string_free(l); + k = buxton_array_get(stored, 2); + free(k); + buxton_array_free(&stored, NULL); + ret = hashmap_put(db, full_key, array); + if (ret != 1) { + abort(); + } + } + + ret = 0; + +end: + return ret; +} + +static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + Hashmap *db; + BuxtonArray *stored; + BuxtonData *d; + BuxtonString *l; + char *full_key = NULL; + int ret; + + assert(layer); + assert(key); + assert(label); + assert(data); + + db = _db_for_resource(layer); + if (!db) { + /* + * Set negative here to indicate layer not found + * rather than key not found, optimization for + * set value + */ + ret = -ENOENT; + goto end; + } + + if (key->name.value) { + if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) { + abort(); + } + } else { + full_key = strdup(key->group.value); + if (!full_key) { + abort(); + } + } + + stored = (BuxtonArray *)hashmap_get(db, full_key); + if (!stored) { + ret = ENOENT; + goto end; + } + d = buxton_array_get(stored, 0); + if (d->type != key->type) { + ret = EINVAL; + goto end; + } + + if (!buxton_data_copy(d, data)) { + abort(); + } + + l = buxton_array_get(stored, 1); + if (!buxton_string_copy(l, label)) { + abort(); + } + + ret = 0; + +end: + free(full_key); + return ret; +} + +static int unset_value(BuxtonLayer *layer, + _BuxtonKey *key, + __attribute__((unused)) BuxtonData *data, + __attribute__((unused)) BuxtonString *label) +{ + Hashmap *db; + BuxtonArray *stored; + BuxtonData *d; + BuxtonString *l; + char *full_key = NULL; + char *k; + int ret; + + assert(layer); + assert(key); + + db = _db_for_resource(layer); + if (!db) { + ret = ENOENT; + goto end; + } + + if (key->name.value) { + if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) { + abort(); + } + } else { + full_key = strdup(key->group.value); + if (!full_key) { + abort(); + } + } + + /* test if the value exists */ + stored = (BuxtonArray *)hashmap_remove(db, full_key); + if (!stored) { + ret = ENOENT; + goto end; + } + + /* free the data */ + d = buxton_array_get(stored, 0); + data_free(d); + l = buxton_array_get(stored, 1); + string_free(l); + k = buxton_array_get(stored, 2); + free(k); + buxton_array_free(&stored, NULL); + + ret = 0; + +end: + free(full_key); + return ret; +} + +_bx_export_ void buxton_module_destroy(void) +{ + const char *key1, *key2; + Iterator iteratori, iteratoro; + Hashmap *map; + BuxtonArray *array; + BuxtonData *d; + BuxtonString *l; + + /* free all hashmaps */ + HASHMAP_FOREACH_KEY(map, key1, _resources, iteratoro) { + HASHMAP_FOREACH_KEY(array, key2, map, iteratori) { + hashmap_remove(map, key2); + d = buxton_array_get(array, 0); + data_free(d); + l = buxton_array_get(array, 1); + string_free(l); + buxton_array_free(&array, NULL); + } + hashmap_remove(_resources, key1); + hashmap_free(map); + free((void *)key1); + map = NULL; + } + hashmap_free(_resources); + _resources = NULL; +} + +_bx_export_ bool buxton_module_init(BuxtonBackend *backend) +{ + + assert(backend); + + /* Point the struct methods back to our own */ + backend->set_value = &set_value; + backend->get_value = &get_value; + backend->unset_value = &unset_value; + backend->list_keys = NULL; + backend->create_db = NULL; + + _resources = hashmap_new(string_hash_func, string_compare_func); + if (!_resources) { + abort(); + } + return true; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/include/buxton.h b/src/include/buxton.h new file mode 100644 index 0000000..8cff5f7 --- /dev/null +++ b/src/include/buxton.h @@ -0,0 +1,380 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file buxton.h Buxton public header + * + * This is the public part of libbuxton + * + * \mainpage Buxton + * \link buxton.h Public API + * \endlink - API listing for libbuxton + * \copyright Copyright (C) 2013 Intel corporation + * \par License + * GNU Lesser General Public License 2.1 + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include + +#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, /** +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "buxtonclient.h" +#include "buxtonkey.h" +#include "buxtonresponse.h" +#include "buxtonstring.h" +#include "configurator.h" +#include "hashmap.h" +#include "log.h" +#include "protocol.h" +#include "util.h" + +static Hashmap *key_hash = NULL; + +int buxton_set_conf_file(char *path) +{ + int r; + struct stat st; + + r = stat(path, &st); + if (r == -1) { + return errno; + } else { + if (st.st_mode & S_IFDIR) { + return EINVAL; + } + } + + buxton_add_cmd_line(CONFIG_CONF_FILE, path); + + return 0; +} + +int buxton_open(BuxtonClient *client) +{ + _BuxtonClient **c = (_BuxtonClient **)client; + _BuxtonClient *cl = NULL; + int bx_socket, r; + struct sockaddr_un remote; + size_t sock_name_len; + + if ((bx_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + return -1; + } + + remote.sun_family = AF_UNIX; + sock_name_len = strlen(buxton_socket()) + 1; + if (sock_name_len >= sizeof(remote.sun_path)) { + buxton_log("Provided socket name: %s is too long, maximum allowed length is %d bytes\n", + buxton_socket(), sizeof(remote.sun_path)); + return -1; + } + + strncpy(remote.sun_path, buxton_socket(), sock_name_len); + r = connect(bx_socket, (struct sockaddr *)&remote, sizeof(remote)); + if ( r == -1) { + close(bx_socket); + return -1; + } + + if (fcntl(bx_socket, F_SETFL, O_NONBLOCK)) { + close(bx_socket); + return -1; + } + + if (!setup_callbacks()) { + close(bx_socket); + return -1; + } + + cl = malloc0(sizeof(_BuxtonClient)); + if (!cl) { + close(bx_socket); + return -1; + } + + cl->fd = bx_socket; + *c = cl; + + return bx_socket; +} + +void buxton_close(BuxtonClient client) +{ + _BuxtonClient *c; + BuxtonKey key = NULL; + Iterator i; + + /* Free all remaining allocated keys */ + HASHMAP_FOREACH_KEY(key, key, key_hash, i) { + hashmap_remove_value(key_hash, key, key); + buxton_key_free(key); + } + + hashmap_free(key_hash); + + if (!client) { + return; + } + + c = (_BuxtonClient *)client; + + cleanup_callbacks(); + close(c->fd); + c->direct = 0; + c->fd = -1; + free(c); +} + +int buxton_get_value(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !(k->group.value) || !(k->name.value) || + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + return EINVAL; + } + + r = buxton_wire_get_value((_BuxtonClient *)client, k, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_register_notification(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !k->group.value || !k->name.value || + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + return EINVAL; + } + + r = buxton_wire_register_notification((_BuxtonClient *)client, k, + callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_unregister_notification(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !k->group.value || !k->name.value || + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + return EINVAL; + } + + r = buxton_wire_unregister_notification((_BuxtonClient *)client, k, + callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_set_value(BuxtonClient client, + BuxtonKey key, + void *value, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !k->group.value || !k->name.value || !k->layer.value || + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || !value) { + return EINVAL; + } + + r = buxton_wire_set_value((_BuxtonClient *)client, k, value, callback, + data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_set_label(BuxtonClient client, + BuxtonKey key, + char *value, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + BuxtonString v; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !k->group.value || !k->layer.value || !value) { + return EINVAL; + } + + k->type = STRING; + v = buxton_string_pack(value); + + r = buxton_wire_set_label((_BuxtonClient *)client, k, &v, callback, + data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_create_group(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + /* We require the key name to be NULL, since it is not used for groups */ + if (!k || !k->group.value || k->name.value || !k->layer.value) { + return EINVAL; + } + + k->type = STRING; + r = buxton_wire_create_group((_BuxtonClient *)client, k, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_remove_group(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + /* We require the key name to be NULL, since it is not used for groups */ + if (!k || !k->group.value || k->name.value || !k->layer.value) { + return EINVAL; + } + + k->type = STRING; + r = buxton_wire_remove_group((_BuxtonClient *)client, k, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_client_list_keys(BuxtonClient client, + char *layer_name, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + BuxtonString l; + + if (!layer_name) { + return EINVAL; + } + + l = buxton_string_pack(layer_name); + + r = buxton_wire_list_keys((_BuxtonClient *)client, &l, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +int buxton_unset_value(BuxtonClient client, + BuxtonKey key, + BuxtonCallback callback, + void *data, + bool sync) +{ + bool r; + int ret = 0; + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k || !k->group.value || !k->name.value || !k->layer.value || + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + return EINVAL; + } + + r = buxton_wire_unset_value((_BuxtonClient *)client, k, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + +BuxtonKey buxton_key_create(char *group, char *name, char *layer, + BuxtonDataType type) +{ + _BuxtonKey *key = NULL; + char *g = NULL; + char *n = NULL; + char *l = NULL; + + if (!group) { + goto fail; + } + + if (type <= BUXTON_TYPE_MIN || type >= BUXTON_TYPE_MAX) { + goto fail; + } + + if (!key_hash) { + /* Create on hashmap on first call to key_create */ + key_hash = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!key_hash) { + return NULL; + } + } + + g = strdup(group); + if (!g) { + goto fail; + } + + if (name) { + n = strdup(name); + if (!n) { + goto fail; + } + } + + if (layer) { + l = strdup(layer); + if (!l) { + goto fail; + } + } + + key = malloc0(sizeof(_BuxtonKey)); + if (!key) { + goto fail; + } + + key->group.value = g; + key->group.length = (uint32_t)strlen(g) + 1; + if (name) { + key->name.value = n; + key->name.length = (uint32_t)strlen(n) + 1; + } else { + key->name.value = NULL; + key->name.length = 0; + } + if (layer) { + key->layer.value = l; + key->layer.length = (uint32_t)strlen(l) + 1; + } else { + key->layer.value = NULL; + key->layer.length = 0; + } + key->type = type; + + /* Add new keys to internal hash for cleanup on close */ + hashmap_put(key_hash, key, key); + + return (BuxtonKey)key; + +fail: + free(g); + free(n); + free(l); + return NULL; +} + +char *buxton_key_get_group(BuxtonKey key) +{ + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!key) { + return NULL; + } + + return get_group(k); +} + +char *buxton_key_get_name(BuxtonKey key) +{ + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!key) { + return NULL; + } + + return get_name(k); +} + +char *buxton_key_get_layer(BuxtonKey key) +{ + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!key) { + return NULL; + } + + return get_layer(k); +} + +BuxtonDataType buxton_key_get_type(BuxtonKey key) +{ + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!key) { + return -1; + } + + return k->type; +} + +void buxton_key_free(BuxtonKey key) +{ + _BuxtonKey *k = (_BuxtonKey *)key; + + if (!k) { + return; + } + + hashmap_remove_value(key_hash, key, key); + + free(k->group.value); + free(k->name.value); + free(k->layer.value); + free(k); +} + +ssize_t buxton_client_handle_response(BuxtonClient client) +{ + return buxton_wire_handle_response((_BuxtonClient *)client); +} + +BuxtonControlMessage buxton_response_type(BuxtonResponse response) +{ + _BuxtonResponse *r = (_BuxtonResponse *)response; + + if (!response) { + return -1; + } + + return r->type; +} + +int32_t buxton_response_status(BuxtonResponse response) +{ + BuxtonData *d; + _BuxtonResponse *r = (_BuxtonResponse *)response; + + if (!response) { + return -1; + } + + if (buxton_response_type(response) == BUXTON_CONTROL_CHANGED) { + return 0; + } + + d = buxton_array_get(r->data, 0); + + if (d) { + return d->store.d_int32; + } + + return -1; +} + +BuxtonKey buxton_response_key(BuxtonResponse response) +{ + _BuxtonKey *key = NULL; + _BuxtonResponse *r = (_BuxtonResponse *)response; + + if (!response) { + return NULL; + } + + if (buxton_response_type(response) == BUXTON_CONTROL_LIST) { + return NULL; + } + + key = malloc0(sizeof(_BuxtonKey)); + if (!key) { + return NULL; + } + + if (!buxton_key_copy(r->key, key)) { + free(key); + return NULL; + } + + return (BuxtonKey)key; +} + +void *buxton_response_value(BuxtonResponse response) +{ + void *p = NULL; + BuxtonData *d = NULL; + _BuxtonResponse *r = (_BuxtonResponse *)response; + BuxtonControlMessage type; + + if (!response) { + return NULL; + } + + type = buxton_response_type(response); + if (type == BUXTON_CONTROL_GET) { + d = buxton_array_get(r->data, 1); + } else if (type == BUXTON_CONTROL_CHANGED) { + if (r->data->len) { + d = buxton_array_get(r->data, 0); + } + } else { + goto out; + } + + if (!d) { + goto out; + } + + switch (d->type) { + case STRING: + return strdup(d->store.d_string.value); + case INT32: + p = malloc0(sizeof(int32_t)); + if (!p) { + goto out; + } + *(int32_t *)p = (int32_t)d->store.d_int32; + break; + case UINT32: + p = malloc0(sizeof(uint32_t)); + if (!p) { + goto out; + } + *(uint32_t *)p = (uint32_t)d->store.d_uint32; + break; + case INT64: + p = malloc0(sizeof(int64_t)); + if (!p) { + goto out; + } + *(int64_t *)p = (int64_t)d->store.d_int64; + break; + case UINT64: + p = malloc0(sizeof(uint64_t)); + if (!p) { + goto out; + } + *(uint64_t *)p = (uint64_t)d->store.d_uint64; + break; + case FLOAT: + p = malloc0(sizeof(float)); + if (!p) { + goto out; + } + *(float *)p = (float)d->store.d_float; + break; + case DOUBLE: + p = malloc0(sizeof(double)); + if (!p) { + goto out; + } + *(double *)p = (double)d->store.d_double; + break; + case BOOLEAN: + p = malloc0(sizeof(bool)); + if (!p) { + goto out; + } + *(bool *)p = (bool)d->store.d_boolean; + break; + default: + break; + } + +out: + return p; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/libbuxton/lbuxton.sym b/src/libbuxton/lbuxton.sym new file mode 100644 index 0000000..37c36d0 --- /dev/null +++ b/src/libbuxton/lbuxton.sym @@ -0,0 +1,27 @@ +BUXTON_1 { + global: + buxton_set_conf_file; + buxton_open; + buxton_close; + buxton_set_value; + buxton_set_label; + buxton_create_group; + buxton_remove_group; + buxton_get_value; + buxton_unset_value; + buxton_register_notification; + buxton_unregister_notification; + buxton_client_handle_response; + buxton_key_get_group; + buxton_key_get_name; + buxton_key_get_layer; + buxton_key_get_type; + buxton_key_create; + buxton_key_free; + buxton_response_status; + buxton_response_type; + buxton_response_key; + buxton_response_value; + local: + *; +}; diff --git a/src/security/smack.c b/src/security/smack.c new file mode 100644 index 0000000..2f85b3f --- /dev/null +++ b/src/security/smack.c @@ -0,0 +1,260 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include + +#include "buxton.h" +#include "buxtonkey.h" +#include "configurator.h" +#include "direct.h" +#include "hashmap.h" +#include "log.h" +#include "smack.h" +#include "util.h" + +static Hashmap *_smackrules = NULL; +/* set to true unless Smack support is not detected by the daemon */ +static bool have_smack = true; + +#define smack_check() do { if (!have_smack) { return true; } } while (0); + + +bool buxton_smack_enabled(void) +{ + return have_smack; +} + +bool buxton_cache_smack_rules(void) +{ + smack_check(); + + FILE *load_file = NULL; + char *rule_pair = NULL; + int ret = true; + bool have_rules = false; + struct stat buf; + + if (_smackrules) { + hashmap_free(_smackrules); + } + + _smackrules = hashmap_new(string_hash_func, string_compare_func); + + if (!_smackrules) { + abort(); + } + + /* FIXME: should check for a proper mount point instead */ + if ((stat(SMACK_MOUNT_DIR, &buf) == -1) || !S_ISDIR(buf.st_mode)) { + buxton_log("Smack filesystem not detected; disabling Smack checks\n"); + have_smack = false; + goto end; + } + + load_file = fopen(buxton_smack_load_file(), "r"); + + if (!load_file) { + switch (errno) { + case ENOENT: + buxton_log("Smackfs load2 file not found; disabling Smack checks\n"); + have_smack = false; + goto end; + default: + buxton_log("fopen(): %m\n"); + ret = false; + goto end; + } + } + + do { + int r; + int chars; + BuxtonKeyAccessType *accesstype; + + char subject[SMACK_LABEL_LEN+1] = { 0, }; + char object[SMACK_LABEL_LEN+1] = { 0, }; + char access[ACC_LEN] = { 0, }; + + /* read contents from load2 */ + chars = fscanf(load_file, "%s %s %s\n", subject, object, access); + + if (ferror(load_file)) { + buxton_log("fscanf(): %m\n"); + ret = false; + goto end; + } + + if (!have_rules && chars == EOF && feof(load_file)) { + buxton_debug("No loaded Smack rules found\n"); + goto end; + } + + if (chars != 3) { + buxton_log("Corrupt load file detected\n"); + ret = false; + goto end; + } + + have_rules = true; + + r = asprintf(&rule_pair, "%s %s", subject, object); + if (r == -1) { + abort(); + } + + accesstype = malloc0(sizeof(BuxtonKeyAccessType)); + if (!accesstype) { + abort(); + } + + *accesstype = ACCESS_NONE; + + if (strchr(access, 'r')) { + *accesstype |= ACCESS_READ; + } + + if (strchr(access, 'w')) { + *accesstype |= ACCESS_WRITE; + } + + hashmap_put(_smackrules, rule_pair, accesstype); + + } while (!feof(load_file)); + +end: + if (load_file) { + fclose(load_file); + } + + return ret; +} + +bool buxton_check_smack_access(BuxtonString *subject, BuxtonString *object, BuxtonKeyAccessType request) +{ + smack_check(); + + _cleanup_free_ char *key = NULL; + int r; + BuxtonKeyAccessType *access; + + assert(subject); + assert(object); + assert((request == ACCESS_READ) || (request == ACCESS_WRITE)); + assert(_smackrules); + + buxton_debug("Subject: %s\n", subject->value); + buxton_debug("Object: %s\n", object->value); + + /* check the builtin Smack rules first */ + if (streq(subject->value, "*")) { + return false; + } + + if (streq(object->value, "@") || streq(subject->value, "@")) { + return true; + } + + if (streq(object->value, "*")) { + return true; + } + + if (streq(subject->value, object->value)) { + return true; + } + + if (request == ACCESS_READ) { + if (streq(object->value, "_")) { + return true; + } + if (streq(subject->value, "^")) { + return true; + } + } + + /* finally, check the loaded rules */ + r = asprintf(&key, "%s %s", subject->value, object->value); + if (r == -1) { + abort(); + } + + buxton_debug("Key: %s\n", key); + + access = hashmap_get(_smackrules, key); + if (!access) { + /* A null value is not an error, since clients may try to + * read/write keys with labels that are not in the loaded + * rule set. In this situation, access is simply denied, + * because there are no further rules to consider. + */ + buxton_debug("Value of key '%s' is NULL\n", key); + return false; + } + + /* After debugging, change this code to: */ + /* return ((*access) & request); */ + if (access) { + buxton_debug("Value: %x\n", *access); + } + + if (request == ACCESS_READ && (*access) & request) { + buxton_debug("Read access granted!\n"); + return true; + } + + if (request == ACCESS_WRITE && ((*access) & ACCESS_READ && (*access) & ACCESS_WRITE)) { + buxton_debug("Write access granted!\n"); + return true; + } + + buxton_debug("Access denied!\n"); + return false; +} + +int buxton_watch_smack_rules(void) +{ + if (!have_smack) { + errno = 0; + return -1; + } + + int fd; + + fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + buxton_log("inotify_init(): %m\n"); + return -1; + } + if (inotify_add_watch(fd, buxton_smack_load_file(), IN_CLOSE_WRITE) < 0) { + buxton_log("inotify_add_watch(): %m\n"); + return -1; + } + return fd; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/security/smack.h b/src/security/smack.h new file mode 100644 index 0000000..c83d06a --- /dev/null +++ b/src/security/smack.h @@ -0,0 +1,95 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "backend.h" +#include "buxton.h" + +/** + * Maximum length for a Smack label + */ +#define SMACK_LABEL_LEN 255 + +/** + * Smack label xattr key + */ +#define SMACK_ATTR_NAME "security.SMACK64" + +/** + * Smackfs mount directory + */ +#define SMACK_MOUNT_DIR "/sys/fs/smackfs" + +/** + * Maximum length of a Smack rule access string + */ +#define ACC_LEN 5 + +/** + * Represents client access to a given resource + */ +typedef enum BuxtonKeyAccessType { + ACCESS_NONE = 0, /** + +#include "configurator.h" +#include "backend.h" +#include "hashmap.h" +#include "util.h" +#include "log.h" +#include "buxtonarray.h" +#include "smack.h" + +/** + * Create a BuxtonLayer out of a ConfigLayer + * + * Validates the data from the config file and creates BuxtonLayer. + * + * @param conf_layer the ConfigLayer to validate + * + * @return a new BuxtonLayer. Callers are responsible for managing + * this memory + */ +static BuxtonLayer *buxton_layer_new(ConfigLayer *conf_layer); + +/* Load layer configurations from disk */ +void buxton_init_layers(BuxtonConfig *config) +{ + Hashmap *layers = NULL; + int nlayers = 0; + ConfigLayer *config_layers = NULL; + int r; + + nlayers = buxton_key_get_layers(&config_layers); + layers = hashmap_new(string_hash_func, string_compare_func); + if (!layers) { + abort(); + } + + for (int n = 0; n < nlayers; n++) { + BuxtonLayer *layer; + + layer = buxton_layer_new(&(config_layers[n])); + if (!layer) { + abort(); + } + + r = hashmap_put(layers, layer->name.value, layer); + if (r != 1) { + abort(); + } + } + + config->layers = layers; + free(config_layers); +} + +static bool is_read_only(ConfigLayer *conf_layer) +{ + return strcmp(conf_layer->access, "read-only") == 0; +} + +static BuxtonLayer *buxton_layer_new(ConfigLayer *conf_layer) +{ + BuxtonLayer *out; + + assert(conf_layer); + out= malloc0(sizeof(BuxtonLayer)); + if (!out) { + abort(); + } + + if (conf_layer->priority < 0) { + goto fail; + } + out->name.value = strdup(conf_layer->name); + if (!out->name.value) { + abort(); + } + out->name.length = (uint32_t)strlen(conf_layer->name); + + if (strcmp(conf_layer->type, "System") == 0) { + out->type = LAYER_SYSTEM; + } else if (strcmp(conf_layer->type, "User") == 0) { + out->type = LAYER_USER; + } else { + buxton_log("Layer %s has unknown type: %s\n", conf_layer->name, conf_layer->type); + goto fail; + } + + if (strcmp(conf_layer->backend, "gdbm") == 0) { + out->backend = BACKEND_GDBM; + } else if (strcmp(conf_layer->backend, "memory") == 0) { + out->backend = BACKEND_MEMORY; + } else { + buxton_log("Layer %s has unknown database: %s\n", conf_layer->name, conf_layer->backend); + goto fail; + } + + if (conf_layer->description != NULL) { + out->description = strdup(conf_layer->description); + if (!out->description) { + abort(); + } + } + + out->readonly = is_read_only(conf_layer); + out->priority = conf_layer->priority; + return out; +fail: + free(out->name.value); + free(out->description); + free(out); + return NULL; +} + +static void init_backend(BuxtonConfig *config, + BuxtonLayer *layer, + BuxtonBackend **backend) +{ + void *handle, *cast; + _cleanup_free_ char *path = NULL; + const char *name; + char *error; + int r; + bool rb; + module_init_func i_func; + module_destroy_func d_func; + BuxtonBackend *backend_tmp; + + assert(layer); + assert(backend); + + if (layer->backend == BACKEND_GDBM) { + name = "gdbm"; + } else if (layer->backend == BACKEND_MEMORY) { + name = "memory"; + } else { + buxton_log("Invalid backend type for layer: %s\n", layer->name); + abort(); + } + + backend_tmp = hashmap_get(config->backends, name); + + if (backend_tmp) { + *backend = backend_tmp; + return; + } + + backend_tmp = malloc0(sizeof(BuxtonBackend)); + if (!backend_tmp) { + abort(); + } + + r = asprintf(&path, "%s/%s.so", buxton_module_dir(), name); + if (r == -1) { + abort(); + } + + /* Load the module */ + handle = dlopen(path, RTLD_LAZY); + + if (!handle) { + buxton_log("dlopen(): %s\n", dlerror()); + abort(); + } + + dlerror(); + cast = dlsym(handle, "buxton_module_init"); + if ((error = dlerror()) != NULL || !cast) { + buxton_log("dlsym(): %s\n", error); + abort(); + } + memcpy(&i_func, &cast, sizeof(i_func)); + dlerror(); + + cast = dlsym(handle, "buxton_module_destroy"); + if ((error = dlerror()) != NULL || !cast) { + buxton_log("dlsym(): %s\n", error); + abort(); + } + memcpy(&d_func, &cast, sizeof(d_func)); + + rb = i_func(backend_tmp); + if (!rb) { + buxton_log("buxton_module_init failed\n"); + abort(); + } + + if (!config->backends) { + config->backends = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!config->backends) { + abort(); + } + } + + r = hashmap_put(config->backends, name, backend_tmp); + if (r != 1) { + abort(); + } + + backend_tmp->module = handle; + backend_tmp->destroy = d_func; + + *backend = backend_tmp; +} + +BuxtonBackend *backend_for_layer(BuxtonConfig *config, + BuxtonLayer *layer) +{ + BuxtonBackend *backend; + int ret; + + assert(layer); + + if (!config->databases) { + config->databases = hashmap_new(string_hash_func, string_compare_func); + if (!config->databases) { + abort(); + } + } + if ((backend = (BuxtonBackend*)hashmap_get(config->databases, layer->name.value)) == NULL) { + /* attempt load of backend */ + init_backend(config, layer, &backend); + + ret = hashmap_put(config->databases, layer->name.value, backend); + if (ret != 1) { + abort(); + } + } + return (BuxtonBackend*)hashmap_get(config->databases, layer->name.value); +} + +void destroy_backend(BuxtonBackend *backend) +{ + + assert(backend); + + backend->set_value = NULL; + backend->get_value = NULL; + backend->list_keys = NULL; + backend->unset_value = NULL; + backend->destroy(); + dlclose(backend->module); + free(backend); + backend = NULL; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/backend.h b/src/shared/backend.h new file mode 100644 index 0000000..edcde53 --- /dev/null +++ b/src/shared/backend.h @@ -0,0 +1,171 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file backend.h Internal header + * This file is used internally by buxton to provide functionality + * used by and for the backend + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#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, /** +#include + +#include "buxtonarray.h" +#include "util.h" + +BuxtonArray *buxton_array_new(void) +{ + BuxtonArray *ret = NULL; + /* If this fails, we simply return NULL and allow the user + * to deal with the error */ + ret = calloc(1, sizeof(BuxtonArray)); + return ret; +} + +bool buxton_array_add(BuxtonArray *array, + void *data) +{ + uint new_len; + size_t curr, new_size; + + if (!array || !data) { + return false; + } + if (!array->data) { + array->data = calloc(1, sizeof(void*)); + if (!array->data) { + return false; + } + } + + new_len = array->len += 1; + curr = (size_t)(array->len*sizeof(void*)); + new_size = curr + sizeof(void*); + if (new_len >= array->len) { + /* Resize the array to hold one more pointer */ + array->data = greedy_realloc((void**)&array->data, &curr, new_size); + if (!array->data) { + return false; + } + } + /* Store the pointer at the end of the array */ + array->len = new_len; + array->data[array->len-1] = data; + return true; +} + +void *buxton_array_get(BuxtonArray *array, uint16_t index) +{ + if (!array) { + return NULL; + } + if (index > array->len) { + return NULL; + } + return array->data[index]; +} + + +void buxton_array_free(BuxtonArray **array, + buxton_free_func free_method) +{ + int i; + if (!array || !*array) { + return; + } + + if (free_method) { + /* Call the free_method on all members */ + for (i = 0; i < (*array)->len; i++) + free_method((*array)->data[i]); + } + /* Ensure this array is indeed gone. */ + free((*array)->data); + free(*array); + *array = NULL; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/buxtonarray.h b/src/shared/buxtonarray.h new file mode 100644 index 0000000..2137bb3 --- /dev/null +++ b/src/shared/buxtonarray.h @@ -0,0 +1,85 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +#include "buxtondata.h" + +/** + * A dynamic array + * Represents daemon's reply to client + */ +typedef struct BuxtonArray { + void **data; /** + +/** + * Used to communicate with Buxton + */ +typedef struct BuxtonClient { + int fd; /**type = STRING; + d->store.d_string.value = s->value; + d->store.d_string.length = s->length; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/buxtonkey.h b/src/shared/buxtonkey.h new file mode 100644 index 0000000..3856060 --- /dev/null +++ b/src/shared/buxtonkey.h @@ -0,0 +1,42 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "buxton.h" +#include "buxtonstring.h" + +/** + * Represents a data key in Buxton + */ +typedef struct BuxtonKey { + BuxtonString group; /** +#include +#include +#include + +#include "buxtonlist.h" + +bool buxton_list_append(BuxtonList **list, void *data) +{ + BuxtonList *head = *list; + + BuxtonList *parent = NULL; + BuxtonList *next = NULL; + + if (!head) { + /* New head generation */ + head = calloc(1, sizeof(BuxtonList)); + if (!head) { + return false; + } + next = parent = head; + head->size = 0; + next->tail = NULL; + } + + if (!next) { + next = calloc(1, sizeof(BuxtonList)); + if (!next) { + return false; + } + if (head->tail) { + parent = head->tail; + } else { + parent = head; + } + parent->next = next; + head->tail = next; + } + head->size += 1; + next->data = data; + *list = head; + return true; +} + +bool buxton_list_prepend(BuxtonList **list, void *data) +{ + BuxtonList *head = *list; + BuxtonList *prev = NULL; + + if (!head) { + /* New head generation */ + head = calloc(1, sizeof(BuxtonList)); + if (!head) { + return false; + } + prev = head; + head->size = 0; + prev->tail = head; + prev->next = NULL; + } else { + /* New item */ + prev = calloc(1, sizeof(BuxtonList)); + if (!prev) { + return false; + } + prev->size = head->size+1; + head->size = 0; + prev->next = head; + prev->tail = head->tail; + head->tail = NULL; + } + /* Previous item is now the head */ + prev->data = data; + *list = prev; + + return true; +} + +bool buxton_list_remove(BuxtonList **list, void *data, bool do_free) +{ + BuxtonList *head = *list; + BuxtonList *current = head; + BuxtonList *prev = head; + + /* Determine the node inside the list */ + while ((current != NULL) && (current->data != data)) { + prev = current; + current = current->next; + } + /* Not found */ + if (!current) { + return false; + } + + /* Data on the head (head removal)*/ + if (current == head) { + if (head->next) { + head->next->size = head->size -1; + head->size = 0; + } + head = head->next; + } else { + /* Middle or end */ + prev->next = current->next; + head->size--; + } + + /* Update tail pointer */ + if (head && head->tail == current) { + head->tail = prev; + head->tail->next = NULL; + } + + /* Should free? */ + if (do_free) { + free(current->data); + } + free(current); + + *list = head; + return true; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/buxtonlist.h b/src/shared/buxtonlist.h new file mode 100644 index 0000000..6471d70 --- /dev/null +++ b/src/shared/buxtonlist.h @@ -0,0 +1,119 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2014 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#pragma once + +#define _GNU_SOURCE +#include +#include +#include + +#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; /**next) + + +/** + * Append data to an existing list, or create a new list if NULL + * + * @note May return false if memory allocation errors exist + * @param list Pointer to BuxtonList pointer + * @param data Data pointer to store in the list + * @return a boolean value, indicating success of the operation + */ +bool buxton_list_append(BuxtonList **list, void *data); + +/* + * Prepend data to an existing list, or create a new list if NULL + * Much faster than append + * + * @note May return false if memory allocation errors exist + * @param list Pointer to BuxtonList pointer + * @param data Data pointer to store in the list + * @return a boolean value, indicating success of the operation + */ +bool buxton_list_prepend(BuxtonList **list, void *data); + +/** + * Remove the given element from the list + * + * @param list Pointer to a BuxtonList pointer + * @param data Remove item with the matching data pointer + * @param do_free Whether to free the item + * @return a boolean value, indicating success of the operation + */ +bool buxton_list_remove(BuxtonList **list, void *data, bool do_free); + +/** + * Free all items in the list + */ +static inline void buxton_list_free(void *p) +{ + BuxtonList *list = *(BuxtonList**)p; + if (!list) { + return; + } + BuxtonList *elem, *node = NULL; + elem = list; + while (elem != NULL) { + node = elem->next; + free(elem); + elem = node; + } +} + +/** + * Free all items in the list and their items + */ +static inline void buxton_list_free_all(void *p) +{ + BuxtonList *list = *(BuxtonList**)p; + if (!list) { + return; + } + BuxtonList *elem, *node = NULL; + elem = list; + while (elem != NULL) { + free(elem->data); + node = elem->next; + free(elem); + elem = node; + } +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/buxtonresponse.h b/src/shared/buxtonresponse.h new file mode 100644 index 0000000..8d432b8 --- /dev/null +++ b/src/shared/buxtonresponse.h @@ -0,0 +1,41 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "buxtonarray.h" +#include "buxtonkey.h" + +/** + * Represents daemon's reply to client + */ +typedef struct BuxtonResponse { + BuxtonArray *data; /** +#include +#include +#include +#include +#include +#include + +#include "configurator.h" +#include "log.h" + +/** + * Section name in ini file for our configuration + */ +#define CONFIG_SECTION "Configuration" + +#ifndef HAVE_SECURE_GETENV +# ifdef HAVE___SECURE_GETENV +# define secure_getenv __secure_getenv +# else +# error neither secure_getenv nor __secure_getenv is available +# endif +#endif + +/** + * @internal + * internal state of configurator + */ +static struct _conf { + bool initialized; /**< track if it's been init'd */ + char* keys[CONFIG_MAX]; /**< bag of values */ + dictionary* ini; /**< ini parser dictionary */ +} conf = { + .initialized = false, + .keys = {0}, + .ini = NULL +}; + +/** + * @internal + * config keys as in environment + * @fixme name sucks + */ +static const char *KS[CONFIG_MAX] = { + NULL, + "BUXTON_CONF_FILE", + "BUXTON_MODULE_DIR", + "BUXTON_DB_PATH", + "BUXTON_SMACK_LOAD_FILE", + "BUXTON_BUXTON_SOCKET" +}; + +/** + * @internal + * keys as they are used in config file + */ +static const char *config_keys[CONFIG_MAX] = { + NULL, + NULL, /**< conf file entry in config file is meaningless */ + "ModuleDirectory", + "DatabasePath", + "SmackLoadFile", + "SocketPath" +}; + +static const char *COMPILE_DEFAULT[CONFIG_MAX] = { + NULL, + _DEFAULT_CONFIGURATION_FILE, + _MODULE_DIRECTORY, + _DB_PATH, + _SMACK_LOAD_FILE, + _BUXTON_SOCKET +}; + +/** + * @internal + * wrap strdup and die if malloc fails + * + * @note This may seem lame and stupid at first glance. The former is + * right the latter is not. This function may abort() + * + * @return pointer to dup'd string + */ +__attribute__ ((pure)) +static inline char *_strdup(const char* string) +{ + char* s; + + s = strdup(string); + if (s == NULL) { + abort(); + } + return s; +} + +/** + * @internal + * grab a string from the ini file + * + * @param section the section of the ini file + * @param name the name of the key + * @param required if key required or not + * @param def default value for nonrequired setting + * + * @note This function may abort() + * + * @return The value as a string. The the string is internal to + * libini, and if at this stage of the game it is null, just die. + * Something is really wrong and even if we could recover, the system + * is not working correctly. + */ +static char *get_ini_string(char *section, char *name, bool required, + char *def) +{ + char buf[PATH_MAX]; + char *s; + + assert(conf.ini); + snprintf(buf, sizeof(buf), "%s:%s", section, name); + s = iniparser_getstring(conf.ini, buf, def); + if (s == NULL && required) { + abort(); + } + return s; +} + +/** + * analagous method to get_ini_string() + * + * @param section the section of the ini file + * @param name the name of the key + * @param required if key required or not + * @param def default value for nonrequired setting + * + * @note inlined b/c only used once and why not. + * + * @return the value + */ +static inline int get_ini_int(char *section, char *name, bool required, + int def) +{ + char buf[PATH_MAX]; + int exists; + + assert(conf.ini); + snprintf(buf, sizeof(buf), "%s:%s", section, name); + exists = iniparser_find_entry(conf.ini, buf); + if (!exists && required) { + abort(); + } + return iniparser_getint(conf.ini, buf, def); +} + +/** + * @internal + * Initialize conf + * + */ +static void initialize(void) +{ + if (conf.initialized) { + return; + } + + for (int i= CONFIG_MIN+1; i < CONFIG_MAX; i++) { + char *envkey; + + /* hasn't been set through command line */ + if (conf.keys[i] == NULL) { + /* second priority is env */ + envkey = secure_getenv(KS[i]); + if (envkey) { + conf.keys[i] = _strdup(envkey); + } + } + + /* third priority is conf file */ + if (conf.keys[i] == NULL && conf.ini) { + /* for once config file is loaded */ + char key[PATH_MAX+strlen(CONFIG_SECTION)+1+1]; /* one for null and one for : */ + char *value; + + snprintf(key, sizeof(key), "%s:%s", CONFIG_SECTION, config_keys[i]); + value = iniparser_getstring(conf.ini, key, NULL); + if (value != NULL) { + conf.keys[i] = _strdup(value); + } + } + + if (conf.keys[i] == NULL) { + /* last priority is compile time defaults */ + conf.keys[i] = _strdup(COMPILE_DEFAULT[i]); + } + + /* if config file is not loaded, load */ + if (i == CONFIG_CONF_FILE) { + char *path = conf.keys[CONFIG_CONF_FILE]; + + conf.ini = iniparser_load(path); + if (conf.ini == NULL) { + buxton_log("Failed to load buxton conf file: %s\n", path); + } + } + } + conf.initialized = true; +} + +/** + * @internal + * use a destructor to free everything rather than yucky at_exit + * nonsense + * + * @return + */ +__attribute__ ((destructor)) static void free_conf(void) +{ + if (!conf.initialized) { + return; + } + for (int i= CONFIG_MIN+1; i < CONFIG_MAX; i++) { + free(conf.keys[i]); + } + iniparser_freedict(conf.ini); +} + +void buxton_add_cmd_line(ConfigKey confkey, char* data) +{ + if (confkey >= CONFIG_MAX || confkey <= CONFIG_MIN) { + buxton_log("invalid confkey"); + return; + } + if (!data) { + buxton_log("invalid data"); + return; + } + + conf.keys[confkey] = _strdup(data); +} + +const char* buxton_module_dir(void) +{ + initialize(); + return (const char*)conf.keys[CONFIG_MODULE_DIR]; +} + +const char* buxton_conf_file(void) +{ + initialize(); + return (const char*)conf.keys[CONFIG_CONF_FILE]; +} + +const char* buxton_db_path(void) +{ + initialize(); + return (const char*)conf.keys[CONFIG_DB_PATH]; +} + +const char* buxton_smack_load_file(void) +{ + initialize(); + return (const char*)conf.keys[CONFIG_SMACK_LOAD_FILE]; +} + +const char* buxton_socket(void) +{ + initialize(); + return (const char*)conf.keys[CONFIG_BUXTON_SOCKET]; +} + +int buxton_key_get_layers(ConfigLayer **layers) +{ + ConfigLayer *_layers; + int n; + int j = 0; + + assert(layers); + initialize(); + if (conf.ini == NULL) { + buxton_log("config file not loaded when calling buxton_key_get_layers()"); + abort(); + } + n = iniparser_getnsec(conf.ini); + _layers = (ConfigLayer*)calloc((size_t)n, sizeof(ConfigLayer)); + if (_layers == NULL) { + abort(); + } + for (int i= 0; i < n; i++) { + char *section_name; + + section_name = iniparser_getsecname(conf.ini, i); + if (!section_name) { + abort(); + } + if (!strcasecmp(section_name, CONFIG_SECTION)) { + continue; + } + _layers[j].name = section_name; + _layers[j].description = get_ini_string(section_name, + "Description", true, NULL); + _layers[j].backend = get_ini_string(section_name, "Backend", + true, NULL); + _layers[j].type = get_ini_string(section_name, "Type", true, + NULL); + _layers[j].priority = get_ini_int(section_name, "Priority", + true, 0); + _layers[j].access = get_ini_string(section_name, "Access", + false, "read-write"); + j++; + } + *layers = _layers; + return j; +} + +void include_configurator(void) +{ + ; +} + + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/configurator.h b/src/shared/configurator.h new file mode 100644 index 0000000..be25a1a --- /dev/null +++ b/src/shared/configurator.h @@ -0,0 +1,134 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file configurator.h Internal header + * This file is used internally by buxton to provide functionality + * used to handle the configuration + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +typedef enum ConfigKey { + CONFIG_MIN = 0, + CONFIG_CONF_FILE, + CONFIG_MODULE_DIR, + CONFIG_DB_PATH, + CONFIG_SMACK_LOAD_FILE, + CONFIG_BUXTON_SOCKET, + CONFIG_MAX +} ConfigKey; + +/** + * Slightly duplicative of BuxtonLayer, but defined here instead of + * there. This will probably be deprecated for BuxtonLayer once + * things are integrated. + */ +typedef struct ConfigLayer { + char *name; + char *type; + char *backend; + char *description; + char *access; + int priority; +} ConfigLayer; + +/** + * @internal + * @brief Add command line data + * + * @note This API is draft + */ +void buxton_add_cmd_line(ConfigKey confkey, char* data); + +/** + * @internal + * @brief Get the directory of plugin modules + * + * @return the path to the plugin module. Do not free this pointer. + * It belongs to configurator. + */ +const char *buxton_module_dir(void) + __attribute__((warn_unused_result)); + +/** + * @internal + * @brief Get the path of the config file + * + * @return the path of the config file. Do not free this pointer. + * It belongs to configurator. + */ +const char *buxton_conf_file(void) + __attribute__((warn_unused_result)); + +/** + * @internal + * @brief Get the path of the buxton database + * + * + * @return the path of the database file. Do not free this pointer. + * It belongs to configurator. + */ +const char *buxton_db_path(void) + __attribute__((warn_unused_result)); + +/** + * @internal + * @brief Get the path of the smack load file. + * + * + * @return the path of the smack load file. Do not free this pointer. + * It belongs to configurator. + */ +const char *buxton_smack_load_file(void) + __attribute__((warn_unused_result)); + +/** + * @internal + * @brief Get the path of the buxton socket. + * + * + * @return the path of the buxton socket. Do not free this pointer. + * It belongs to configurator. + */ +const char *buxton_socket(void) + __attribute__((warn_unused_result)); + +/** + * @internal + * @brief Get an array of ConfigLayers from the conf file + * + * @param layers pointer to a pointer where the array of ConfigLayers + * will be stored. Callers should free this pointer with free when + * they are done with it. + * + * @return an integer that indicates the number of layers. + */ +int buxton_key_get_layers(ConfigLayer **layers) + __attribute__((warn_unused_result)); + +void include_configurator(void); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/dictionary.c b/src/shared/dictionary.c new file mode 100644 index 0000000..058f4e0 --- /dev/null +++ b/src/shared/dictionary.c @@ -0,0 +1,425 @@ +/* + Copyright (c) 2000-2011 by Nicolas Devillard. + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.c + @author N. Devillard + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "dictionary.h" + +#include +#include +#include +#include + +/** 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>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 (sizesize = 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; in != 0) { + printf("error deleting values\n"); + } + printf("deallocating...\n"); + dictionary_del(d); + return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/src/shared/dictionary.h b/src/shared/dictionary.h new file mode 100644 index 0000000..1f840cb --- /dev/null +++ b/src/shared/dictionary.h @@ -0,0 +1,188 @@ +/* + Copyright (c) 2000-2011 by Nicolas Devillard. + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.h + @author N. Devillard + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +/*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ +/*-------------------------------------------------------------------------*/ +typedef struct _dictionary_ { + int n ; /** Number of entries in dictionary */ + size_t size ; /** Storage size */ + char ** val ; /** List of string values */ + char ** key ; /** List of string keys */ + unsigned * hash ; /** List of hash values for keys */ +} dictionary ; + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key); + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(size_t size); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * vd); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, const char * key, char * def); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * vd, const char * key, const char * val); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out); diff --git a/src/shared/direct.c b/src/shared/direct.c new file mode 100644 index 0000000..878ad7b --- /dev/null +++ b/src/shared/direct.c @@ -0,0 +1,719 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include + +#include "direct.h" +#include "log.h" +#include "smack.h" +#include "util.h" + +#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK" + +bool buxton_direct_open(BuxtonControl *control) +{ + + assert(control); + + memzero(&(control->config), sizeof(BuxtonConfig)); + buxton_init_layers(&(control->config)); + + control->client.direct = true; + control->client.pid = getpid(); + + return true; +} + +int32_t buxton_direct_get_value(BuxtonControl *control, _BuxtonKey *key, + BuxtonData *data, BuxtonString *data_label, + BuxtonString *client_label) +{ + /* Handle direct manipulation */ + BuxtonLayer *l; + BuxtonConfig *config; + BuxtonString layer = (BuxtonString){ NULL, 0 }; + Iterator i; + BuxtonData d; + int priority = 0; + int32_t ret; + BuxtonLayerType layer_origin = -1; + + assert(control); + assert(key); + + if (key->layer.value) { + ret = (int32_t)buxton_direct_get_value_for_layer(control, key, data, + data_label, + client_label); + return ret; + } + + config = &control->config; + + HASHMAP_FOREACH(l, config->layers, i) { + key->layer.value = l->name.value; + key->layer.length = l->name.length; + ret = (int32_t)buxton_direct_get_value_for_layer(control, + key, + &d, + data_label, + client_label); + if (!ret) { + free(data_label->value); + data_label->value = NULL; + data_label->length = 0; + if (d.type == STRING) { + free(d.store.d_string.value); + } + + if ((l->type == LAYER_SYSTEM && (layer_origin != LAYER_SYSTEM || + priority <= l->priority)) || + (l->type == LAYER_USER && layer_origin != LAYER_SYSTEM && + priority <= l->priority)) { + if (l->type == LAYER_SYSTEM) { + layer_origin = LAYER_SYSTEM; + } else { + layer_origin = LAYER_USER; + } + priority = l->priority; + layer.value = l->name.value; + layer.length = l->name.length; + } + } + } + if (layer.value) { + key->layer.value = layer.value; + key->layer.length = layer.length; + ret = (int32_t)buxton_direct_get_value_for_layer(control, + key, + data, + data_label, + client_label); + key->layer.value = NULL; + key->layer.length = 0; + + return ret; + } + return ENOENT; +} + +int buxton_direct_get_value_for_layer(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *data_label, + BuxtonString *client_label) +{ + /* Handle direct manipulation */ + BuxtonBackend *backend = NULL; + BuxtonLayer *layer = NULL; + BuxtonConfig *config; + BuxtonData g; + _BuxtonKey group; + BuxtonString group_label; + int ret; + + assert(control); + assert(key); + assert(data_label); + + buxton_debug("get_value '%s:%s' for layer '%s' start\n", + key->group.value, key->name.value, key->layer.value); + + memzero(&g, sizeof(BuxtonData)); + memzero(&group, sizeof(_BuxtonKey)); + memzero(&group_label, sizeof(BuxtonString)); + + if (!key->layer.value) { + ret = EINVAL; + goto fail; + } + + config = &control->config; + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + ret = EINVAL; + goto fail; + } + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + + /* Groups must be created first, so bail if this key's group doesn't exist */ + if (key->name.value) { + if (!buxton_copy_key_group(key, &group)) { + abort(); + } + ret = buxton_direct_get_value_for_layer(control, &group, &g, &group_label, NULL); + if (ret) { + buxton_debug("Group %s for name %s missing for get value\n", key->group.value, key->name.value); + goto fail; + } + } + + /* The group checks are only needed for key lookups, or we recurse endlessly */ + if (key->name.value && client_label) { + if (!buxton_check_smack_access(client_label, &group_label, ACCESS_READ)) { + ret = EPERM; + goto fail; + } + } + + ret = backend->get_value(layer, key, data, data_label); + if (!ret) { + /* Access checks are not needed for direct clients, where client_label is NULL */ + if (data_label->value && client_label && client_label->value && + !buxton_check_smack_access(client_label, data_label, ACCESS_READ)) { + /* Client lacks permission to read the value */ + free(data_label->value); + ret = EPERM; + goto fail; + } + } + +fail: + free(g.store.d_string.value); + free(group.group.value); + free(group.name.value); + free(group.layer.value); + free(group_label.value); + buxton_debug("get_value '%s:%s' for layer '%s' end\n", + key->group.value, key->name.value, key->layer.value); + return ret; +} + +bool buxton_direct_set_value(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *label) +{ + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonConfig *config; + BuxtonString default_label = buxton_string_pack("_"); + BuxtonString *l; + _cleanup_buxton_data_ BuxtonData *d = NULL; + _cleanup_buxton_data_ BuxtonData *g = NULL; + _cleanup_buxton_key_ _BuxtonKey *group = NULL; + _cleanup_buxton_string_ BuxtonString *data_label = NULL; + _cleanup_buxton_string_ BuxtonString *group_label = NULL; + bool r = false; + int ret; + + assert(control); + assert(key); + assert(data); + + buxton_debug("set_value start\n"); + + group = malloc0(sizeof(_BuxtonKey)); + if (!group) { + abort(); + } + g = malloc0(sizeof(BuxtonData)); + if (!g) { + abort(); + } + group_label = malloc0(sizeof(BuxtonString)); + if (!group_label) { + abort(); + } + + d = malloc0(sizeof(BuxtonData)); + if (!d) { + abort(); + } + data_label = malloc0(sizeof(BuxtonString)); + if (!data_label) { + abort(); + } + + /* Groups must be created first, so bail if this key's group doesn't exist */ + if (!buxton_copy_key_group(key, group)) { + abort(); + } + + ret = buxton_direct_get_value_for_layer(control, group, g, group_label, NULL); + if (ret) { + buxton_debug("Error(%d): %s\n", ret, strerror(ret)); + buxton_debug("Group %s for name %s missing for set value\n", key->group.value, key->name.value); + goto fail; + } + + /* Access checks are not needed for direct clients, where label is NULL */ + if (label) { + if (!buxton_check_smack_access(label, group_label, ACCESS_WRITE)) { + goto fail; + } + + ret = buxton_direct_get_value_for_layer(control, key, d, data_label, NULL); + if (ret == -ENOENT || ret == EINVAL) { + goto fail; + } + if (!ret) { + if (!buxton_check_smack_access(label, data_label, ACCESS_WRITE)) { + goto fail; + } + l = data_label; + } else { + l = label; + } + } else { + ret = buxton_direct_get_value_for_layer(control, key, d, data_label, NULL); + if (ret == -ENOENT || ret == EINVAL) { + goto fail; + } else if (!ret) { + l = data_label; + } else { + l = &default_label; + } + } + + config = &control->config; + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + goto fail; + } + + if (layer->readonly) { + buxton_debug("Read-only layer!\n"); + goto fail; + } + + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + ret = backend->set_value(layer, key, data, l); + if (ret) { + buxton_debug("set value failed: %s\n", strerror(ret)); + } else { + r = true; + } + +fail: + buxton_debug("set_value end\n"); + return r; +} + +bool buxton_direct_set_label(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) +{ + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonConfig *config; + bool r = false; + int ret; + + assert(control); + assert(key); + assert(label); + + config = &control->config; + + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + goto fail; + } + + if (layer->readonly) { + buxton_debug("Read-only layer!\n"); + goto fail; + } + + if (layer->type == LAYER_SYSTEM) { + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + /* FIXME: should check client's capability set instead of UID */ + if (control->client.uid != 0 && !skip_check) { + buxton_debug("Not permitted to create group '%s'\n", key->group.value); + goto fail; + } + } else { + buxton_debug("Cannot set labels in a user layer\n"); + goto fail; + } + + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + ret = backend->set_value(layer, key, NULL, label); + if (ret) { + buxton_debug("set label failed: %s\n", strerror(ret)); + } else { + r = true; + } + +fail: + return r; +} + +bool buxton_direct_create_group(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) +{ + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonConfig *config; + BuxtonString s, l; + _cleanup_buxton_data_ BuxtonData *data = NULL; + _cleanup_buxton_data_ BuxtonData *group = NULL; + _cleanup_buxton_string_ BuxtonString *dlabel = NULL; + _cleanup_buxton_string_ BuxtonString *glabel = NULL; + bool r = false; + int ret; + + assert(control); + assert(key); + + data = malloc0(sizeof(BuxtonData)); + if (!data) { + abort(); + } + group = malloc0(sizeof(BuxtonData)); + if (!group) { + abort(); + } + dlabel = malloc0(sizeof(BuxtonString)); + if (!dlabel) { + abort(); + } + glabel = malloc0(sizeof(BuxtonString)); + if (!glabel) { + abort(); + } + + config = &control->config; + + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + goto fail; + } + + if (layer->readonly) { + buxton_debug("Read-only layer!\n"); + goto fail; + } + + if (layer->type == LAYER_SYSTEM) { + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + /* FIXME: should check client's capability set instead of UID */ + if (control->client.uid != 0 && !skip_check) { + buxton_debug("Not permitted to create group '%s'\n", key->group.value); + goto fail; + } + } + + if (buxton_direct_get_value_for_layer(control, key, group, glabel, NULL) != ENOENT) { + buxton_debug("Group '%s' already exists\n", key->group.value); + goto fail; + } + + backend = backend_for_layer(config, layer); + assert(backend); + + /* Since groups don't have a value, we create a dummy value */ + data->type = STRING; + s = buxton_string_pack("BUXTON_GROUP_VALUE"); + if (!buxton_string_copy(&s, &data->store.d_string)) { + abort(); + } + + if (label) { + if (!buxton_string_copy(label, dlabel)) { + abort(); + } + } else { + /* _ (floor) is our current default label */ + l = buxton_string_pack("_"); + if (!buxton_string_copy(&l, dlabel)) { + abort(); + } + } + + layer->uid = control->client.uid; + ret = backend->set_value(layer, key, data, dlabel); + if (ret) { + buxton_debug("create group failed: %s\n", strerror(ret)); + } else { + r = true; + } + +fail: + return r; +} + +bool buxton_direct_remove_group(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *client_label) +{ + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonConfig *config; + _cleanup_buxton_data_ BuxtonData *group = NULL; + _cleanup_buxton_string_ BuxtonString *glabel = NULL; + bool r = false; + int ret; + + assert(control); + assert(key); + + group = malloc0(sizeof(BuxtonData)); + if (!group) { + abort(); + } + glabel = malloc0(sizeof(BuxtonString)); + if (!glabel) { + abort(); + } + + config = &control->config; + + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + goto fail; + } + + if (layer->readonly) { + buxton_debug("Read-ony layer!\n"); + goto fail; + } + + if (layer->type == LAYER_SYSTEM) { + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + /* FIXME: should check client's capability set instead of UID */ + if (control->client.uid != 0 && !skip_check) { + buxton_debug("Not permitted to remove group '%s'\n", key->group.value); + goto fail; + } + } + + if (buxton_direct_get_value_for_layer(control, key, group, glabel, NULL)) { + buxton_debug("Group '%s' doesn't exist\n", key->group.value); + goto fail; + } + + if (layer->type == LAYER_USER) { + if (client_label && !buxton_check_smack_access(client_label, glabel, ACCESS_WRITE)) { + goto fail; + } + } + + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + + ret = backend->unset_value(layer, key, NULL, NULL); + if (ret) { + buxton_debug("remove group failed: %s\n", strerror(ret)); + } else { + r = true; + } + +fail: + return r; +} + +bool buxton_direct_list_keys(BuxtonControl *control, + BuxtonString *layer_name, + BuxtonArray **list) +{ + assert(control); + assert(layer_name); + + /* Handle direct manipulation */ + BuxtonBackend *backend = NULL; + BuxtonLayer *layer; + BuxtonConfig *config; + + config = &control->config; + if ((layer = hashmap_get(config->layers, layer_name->value)) == NULL) { + return false; + } + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + return backend->list_keys(layer, list); +} + +bool buxton_direct_unset_value(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) +{ + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonConfig *config; + _cleanup_buxton_string_ BuxtonString *data_label = NULL; + _cleanup_buxton_string_ BuxtonString *group_label = NULL; + _cleanup_buxton_data_ BuxtonData *d = NULL; + _cleanup_buxton_data_ BuxtonData *g = NULL; + _cleanup_buxton_key_ _BuxtonKey *group = NULL; + int ret; + bool r = false; + + assert(control); + assert(key); + + group = malloc0(sizeof(_BuxtonKey)); + if (!group) { + abort(); + } + g = malloc0(sizeof(BuxtonData)); + if (!g) { + abort(); + } + group_label = malloc0(sizeof(BuxtonString)); + if (!group_label) { + abort(); + } + + d = malloc0(sizeof(BuxtonData)); + if (!d) { + abort(); + } + data_label = malloc0(sizeof(BuxtonString)); + if (!data_label) { + abort(); + } + + if (!buxton_copy_key_group(key, group)) { + abort(); + } + + if (buxton_direct_get_value_for_layer(control, group, g, group_label, NULL)) { + buxton_debug("Group %s for name %s missing for unset value\n", key->group.value, key->name.value); + goto fail; + } + + /* Access checks are not needed for direct clients, where label is NULL */ + if (label) { + if (!buxton_check_smack_access(label, group_label, ACCESS_WRITE)) { + goto fail; + } + if (!buxton_direct_get_value_for_layer(control, key, d, data_label, NULL)) { + if (!buxton_check_smack_access(label, data_label, ACCESS_WRITE)) { + goto fail; + } + } else { + buxton_debug("Key %s not found, so unset fails\n", key->name.value); + goto fail; + } + } + + config = &control->config; + if ((layer = hashmap_get(config->layers, key->layer.value)) == NULL) { + return false; + } + + if (layer->readonly) { + buxton_debug("Read-only layer!\n"); + return false; + } + backend = backend_for_layer(config, layer); + assert(backend); + + layer->uid = control->client.uid; + ret = backend->unset_value(layer, key, NULL, NULL); + if (ret) { + buxton_debug("Unset value failed: %s\n", strerror(ret)); + } else { + r = true; + } + +fail: + return r; +} + +bool buxton_direct_init_db(BuxtonControl *control, BuxtonString *layer_name) +{ + BuxtonBackend *backend; + BuxtonConfig *config; + BuxtonLayer *layer; + bool ret = false; + void *db; + + assert(control); + assert(layer_name); + + config = &control->config; + layer = hashmap_get(config->layers, layer_name->value); + if (!layer) { + goto end; + } + + if (layer->type == LAYER_USER) { + ret = true; + goto end; + } + + backend = backend_for_layer(config, layer); + assert(backend); + + db = backend->create_db(layer); + if (db) { + ret = true; + } + +end: + return ret; +} + +void buxton_direct_close(BuxtonControl *control) +{ + Iterator iterator; + BuxtonBackend *backend; + BuxtonLayer *layer; + BuxtonString *key; + + control->client.direct = false; + + HASHMAP_FOREACH(backend, control->config.backends, iterator) { + destroy_backend(backend); + } + hashmap_free(control->config.backends); + hashmap_free(control->config.databases); + + HASHMAP_FOREACH_KEY(layer, key, control->config.layers, iterator) { + hashmap_remove(control->config.layers, key); + free(layer->name.value); + free(layer->description); + free(layer); + } + hashmap_free(control->config.layers); + + control->client.direct = false; + control->config.backends = NULL; + control->config.databases = NULL; + control->config.layers = NULL; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/direct.h b/src/shared/direct.h new file mode 100644 index 0000000..00b5ee7 --- /dev/null +++ b/src/shared/direct.h @@ -0,0 +1,169 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file direct.h Internal header + * This file is used internally by buxton to provide functionality + * used by the daemon and buxtonctl for talking to the backend + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include "buxton.h" +#include "hashmap.h" + +/** + * Open a direct connection to Buxton + * + * @param control Valid BuxtonControl instance + * @return a boolean value, indicating success of the operation + */ +bool buxton_direct_open(BuxtonControl *control) + __attribute__((warn_unused_result)); + +/** + * Create a DB for a given layer in Buxton + * + * @param control Valid BuxtonControl instance + * @param layer_name BuxtonString of the layer name to create + * @return a boolean value, indicating success of the operation + */ +bool buxton_direct_init_db(BuxtonControl *control, BuxtonString *layer_name) + __attribute__((warn_unused_result)); + +/** + * Close direct Buxton management connection + * @param control Valid BuxtonControl instance + */ +void buxton_direct_close(BuxtonControl *control); + +/** + * Set a value within Buxton + * @param control An initialized control structure + * @param key The key struct + * @param label A BuxtonString containing the label to set + * @return A boolean value, indicating success of the operation + */ +bool buxton_direct_set_label(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) + __attribute__((warn_unused_result)); + +/** + * Create a group within Buxton + * @param control An initialized control structure + * @param key The key struct with group and layer members initialized + * @param label The Smack label of the client + * @return A boolean value, indicating success of the operation + */ +bool buxton_direct_create_group(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) + __attribute__((warn_unused_result)); + +/** + * Remove a group within Buxton + * @param control An initialized control structure + * @param key The key struct with group and layer members initialized + * @param client_label The Smack label of the client + * @return A boolean value, indicating success of the operation + */ +bool buxton_direct_remove_group(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *client_label) + __attribute__((warn_unused_result)); + +/** + * Set a value within Buxton + * @param control An initialized control structure + * @param key The key struct + * @param data A struct containing the data to set + * @param label The Smack label for the client + * @return A boolean value, indicating success of the operation + */ +bool buxton_direct_set_value(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *label) + __attribute__((warn_unused_result)); + +/** + * Retrieve a value from Buxton + * @param control An initialized control structure + * @param key The key to retrieve + * @param data An empty BuxtonData, where data is stored + * @param data_label The Smack label of the data + * @param client_label The Smack label of the client + * @return A int32_t value, indicating success of the operation + */ +int32_t buxton_direct_get_value(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *data_label, + BuxtonString *client_label) + __attribute__((warn_unused_result)); + +/** + * Retrieve a value from Buxton by layer + * @param control An initialized control structure + * @param key The key to retrieve + * @param data An empty BuxtonData, where data is stored + * @param data_label The Smack label of the data + * @param client_label The Smack label of the client + * @return An int value, indicating success of the operation + */ +int buxton_direct_get_value_for_layer(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *data_label, + BuxtonString *client_label) + __attribute__((warn_unused_result)); + +/** + * Retrieve a list of keys from Buxton + * @param control An initialized control structure + * @param layer_name The layer to pquery + * @param data An empty BuxtonArray, where results are stored + * @return A boolean value, indicating success of the operation + */ +bool buxton_direct_list_keys(BuxtonControl *control, + BuxtonString *layer, + BuxtonArray **list) + __attribute__((warn_unused_result)); + +/** + * Unset a value by key in the given BuxtonLayer + * @param control An initialized control structure + * @param key The key to remove + * @param label The Smack label of the client + * @return a boolean value, indicating success of the operation + */ +bool buxton_direct_unset_value(BuxtonControl *control, + _BuxtonKey *key, + BuxtonString *label) + __attribute__((warn_unused_result)); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c new file mode 100644 index 0000000..5a77440 --- /dev/null +++ b/src/shared/hashmap.c @@ -0,0 +1,928 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include + +#include "util.h" +#include "hashmap.h" +#include "macro.h" + +#define INITIAL_N_BUCKETS 31 + +struct hashmap_entry { + const void *key; + void *value; + struct hashmap_entry *bucket_next, *bucket_previous; + struct hashmap_entry *iterate_next, *iterate_previous; +}; + +struct Hashmap { + hash_func_t hash_func; + compare_func_t compare_func; + + struct hashmap_entry *iterate_list_head, *iterate_list_tail; + + struct hashmap_entry ** buckets; + unsigned n_buckets, n_entries; + + bool from_pool; +}; + +struct pool { + struct pool *next; + unsigned n_tiles; + unsigned n_used; +}; + +static struct pool *first_hashmap_pool = NULL; +static void *first_hashmap_tile = NULL; + +static struct pool *first_entry_pool = NULL; +static void *first_entry_tile = NULL; + +static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) { + unsigned i; + + /* When a tile is released we add it to the list and simply + * place the next pointer at its offset 0. */ + + assert(tile_size >= sizeof(void*)); + + if (*first_tile) { + void *r; + + r = *first_tile; + *first_tile = * (void**) (*first_tile); + return r; + } + + if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) { + unsigned n; + size_t size; + struct pool *p; + + n = *first_pool ? (*first_pool)->n_tiles : 0; + n = MAX(512U, n * 2); + size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size); + n = (unsigned)((size - ALIGN(sizeof(struct pool))) / tile_size); + + p = malloc0(size); + if (!p) + return NULL; + + p->next = *first_pool; + p->n_tiles = n; + p->n_used = 0; + + *first_pool = p; + } + + i = (*first_pool)->n_used++; + + return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size; +} + +static void deallocate_tile(void **first_tile, void *p) { + * (void**) p = *first_tile; + *first_tile = p; +} + +#ifdef VALGRIND + +static void drop_pool(struct pool *p) { + while (p) { + struct pool *n; + n = p->next; + free(p); + p = n; + } +} + +__attribute__((destructor)) static void cleanup_pool(void) { + /* Be nice to valgrind */ + + drop_pool(first_hashmap_pool); + drop_pool(first_entry_pool); +} + +#endif + +unsigned string_hash_func(const void *p) { + unsigned hash = 5381; + const signed char *c; + + /* DJB's hash function */ + + for (c = p; *c; c++) + hash = (hash << 5) + hash + (unsigned) *c; + + return hash; +} + +int string_compare_func(const void *a, const void *b) { + return strcmp(a, b); +} + +unsigned trivial_hash_func(const void *p) { + return PTR_TO_UINT(p); +} + +int trivial_compare_func(const void *a, const void *b) { + return a < b ? -1 : (a > b ? 1 : 0); +} + +unsigned uint64_hash_func(const void *p) { + uint64_t u; + + assert(sizeof(uint64_t) == 2*sizeof(unsigned)); + + u = *(const uint64_t*) p; + + return (unsigned) ((u >> 32) ^ u); +} + +int uint64_compare_func(const void *_a, const void *_b) { + uint64_t a, b; + + a = *(const uint64_t*) _a; + b = *(const uint64_t*) _b; + + return a < b ? -1 : (a > b ? 1 : 0); +} + +Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { + Hashmap *h; + size_t size; + + size = ALIGN(sizeof(Hashmap)) + INITIAL_N_BUCKETS * sizeof(struct hashmap_entry*); + + h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size); + if (!h) + return NULL; + + h->hash_func = hash_func ? hash_func : trivial_hash_func; + h->compare_func = compare_func ? compare_func : trivial_compare_func; + + h->n_buckets = INITIAL_N_BUCKETS; + h->n_entries = 0; + h->iterate_list_head = h->iterate_list_tail = NULL; + + h->buckets = (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))); + h->from_pool = true; + + return h; +} + +int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) { + Hashmap *q; + + assert(h); + + if (*h) + return 0; + + q = hashmap_new(hash_func, compare_func); + if (!q) + return -ENOMEM; + *h = q; + return 0; +} + +static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { + assert(h); + assert(e); + + /* Insert into hash table */ + e->bucket_next = h->buckets[hash]; + e->bucket_previous = NULL; + if (h->buckets[hash]) + h->buckets[hash]->bucket_previous = e; + h->buckets[hash] = e; + + /* Insert into iteration list */ + e->iterate_previous = h->iterate_list_tail; + e->iterate_next = NULL; + if (h->iterate_list_tail) { + assert(h->iterate_list_head); + h->iterate_list_tail->iterate_next = e; + } else { + assert(!h->iterate_list_head); + h->iterate_list_head = e; + } + h->iterate_list_tail = e; + + h->n_entries++; + assert(h->n_entries >= 1); +} + +static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { + assert(h); + assert(e); + + /* Remove from iteration list */ + if (e->iterate_next) + e->iterate_next->iterate_previous = e->iterate_previous; + else + h->iterate_list_tail = e->iterate_previous; + + if (e->iterate_previous) + e->iterate_previous->iterate_next = e->iterate_next; + else + h->iterate_list_head = e->iterate_next; + + /* Remove from hash table bucket list */ + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else + h->buckets[hash] = e->bucket_next; + + assert(h->n_entries >= 1); + h->n_entries--; +} + +static void remove_entry(Hashmap *h, struct hashmap_entry *e) { + unsigned hash; + + assert(h); + assert(e); + + hash = h->hash_func(e->key) % h->n_buckets; + + unlink_entry(h, e, hash); + + if (h->from_pool) + deallocate_tile(&first_entry_tile, e); + else + free(e); +} + +void hashmap_free(Hashmap*h) { + + /* Free the hashmap, but nothing in it */ + + if (!h) + return; + + hashmap_clear(h); + + if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)))) + free(h->buckets); + + if (h->from_pool) + deallocate_tile(&first_hashmap_tile, h); + else + free(h); +} + +void hashmap_free_free(Hashmap *h) { + + /* Free the hashmap and all data objects in it, but not the + * keys */ + + if (!h) + return; + + hashmap_clear_free(h); + hashmap_free(h); +} + +void hashmap_free_free_free(Hashmap *h) { + + /* Free the hashmap and all data and key objects in it */ + + if (!h) + return; + + hashmap_clear_free_free(h); + hashmap_free(h); +} + +void hashmap_clear(Hashmap *h) { + if (!h) + return; + + while (h->iterate_list_head) + remove_entry(h, h->iterate_list_head); +} + +void hashmap_clear_free(Hashmap *h) { + void *p; + + if (!h) + return; + + while ((p = hashmap_steal_first(h))) + free(p); +} + +void hashmap_clear_free_free(Hashmap *h) { + if (!h) + return; + + while (h->iterate_list_head) { + void *a, *b; + + a = h->iterate_list_head->value; + b = (void*) h->iterate_list_head->key; + remove_entry(h, h->iterate_list_head); + free(a); + free(b); + } +} + + +static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + assert(h); + assert(hash < h->n_buckets); + + for (e = h->buckets[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +static bool resize_buckets(Hashmap *h) { + unsigned m; + struct hashmap_entry **n, *i; + + assert(h); + + if (_likely_(h->n_entries*4 < h->n_buckets*3)) + return false; + + /* Increase by four */ + m = (h->n_entries+1)*4-1; + + /* If we hit OOM we simply risk packed hashmaps... */ + n = new0(struct hashmap_entry*, m); + if (!n) + return false; + + for (i = h->iterate_list_head; i; i = i->iterate_next) { + unsigned hash, x; + + hash = h->hash_func(i->key); + + /* First, drop from old bucket table */ + if (i->bucket_next) + i->bucket_next->bucket_previous = i->bucket_previous; + + if (i->bucket_previous) + i->bucket_previous->bucket_next = i->bucket_next; + else + h->buckets[hash % h->n_buckets] = i->bucket_next; + + /* Then, add to new backet table */ + x = hash % m; + + i->bucket_next = n[x]; + i->bucket_previous = NULL; + if (n[x]) + n[x]->bucket_previous = i; + n[x] = i; + } + + if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)))) + free(h->buckets); + + h->buckets = n; + h->n_buckets = m; + + return true; +} + +int hashmap_put(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (e) { + if (e->value == value) + return 0; + return -EEXIST; + } + + if (resize_buckets(h)) + hash = h->hash_func(key) % h->n_buckets; + + if (h->from_pool) + e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry)); + else + e = new(struct hashmap_entry, 1); + + if (!e) + return -ENOMEM; + + e->key = key; + e->value = value; + + link_entry(h, e, hash); + + return 1; +} + +int hashmap_replace(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (e) { + e->key = key; + e->value = value; + return 0; + } + + return hashmap_put(h, key, value); +} + +int hashmap_update(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (!e) + return -ENOENT; + + e->value = value; + return 0; +} + +void* hashmap_get(Hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (!e) + return NULL; + + return e->value; +} + +void* hashmap_get2(Hashmap *h, const void *key, void **key2) { + unsigned hash; + struct hashmap_entry *e; + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (!e) + return NULL; + + if (key2) + *key2 = (void*) e->key; + + return e->value; +} + +bool hashmap_contains(Hashmap *h, const void *key) { + unsigned hash; + + if (!h) + return false; + + hash = h->hash_func(key) % h->n_buckets; + + if (!hash_scan(h, hash, key)) + return false; + + return true; +} + +void* hashmap_remove(Hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + void *data; + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + data = e->value; + remove_entry(h, e); + + return data; +} + +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct hashmap_entry *e; + unsigned old_hash, new_hash; + + if (!h) + return -ENOENT; + + old_hash = h->hash_func(old_key) % h->n_buckets; + if (!(e = hash_scan(h, old_hash, old_key))) + return -ENOENT; + + new_hash = h->hash_func(new_key) % h->n_buckets; + if (hash_scan(h, new_hash, new_key)) + return -EEXIST; + + unlink_entry(h, e, old_hash); + + e->key = new_key; + e->value = value; + + link_entry(h, e, new_hash); + + return 0; +} + +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct hashmap_entry *e, *k; + unsigned old_hash, new_hash; + + if (!h) + return -ENOENT; + + old_hash = h->hash_func(old_key) % h->n_buckets; + if (!(e = hash_scan(h, old_hash, old_key))) + return -ENOENT; + + new_hash = h->hash_func(new_key) % h->n_buckets; + if ((k = hash_scan(h, new_hash, new_key))) + if (e != k) + remove_entry(h, k); + + unlink_entry(h, e, old_hash); + + e->key = new_key; + e->value = value; + + link_entry(h, e, new_hash); + + return 0; +} + +void* hashmap_remove_value(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + + e = hash_scan(h, hash, key); + if (!e) + return NULL; + + if (e->value != value) + return NULL; + + remove_entry(h, e); + + return value; +} + +void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) { + struct hashmap_entry *e; + + assert(i); + + if (!h) + goto at_end; + + if (*i == ITERATOR_LAST) + goto at_end; + + if (*i == ITERATOR_FIRST && !h->iterate_list_head) + goto at_end; + + e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i; + + if (e->iterate_next) + *i = (Iterator) e->iterate_next; + else + *i = ITERATOR_LAST; + + if (key) + *key = e->key; + + return e->value; + +at_end: + *i = ITERATOR_LAST; + + if (key) + *key = NULL; + + return NULL; +} + +void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) { + struct hashmap_entry *e; + + assert(i); + + if (!h) + goto at_beginning; + + if (*i == ITERATOR_FIRST) + goto at_beginning; + + if (*i == ITERATOR_LAST && !h->iterate_list_tail) + goto at_beginning; + + e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i; + + if (e->iterate_previous) + *i = (Iterator) e->iterate_previous; + else + *i = ITERATOR_FIRST; + + if (key) + *key = e->key; + + return e->value; + +at_beginning: + *i = ITERATOR_FIRST; + + if (key) + *key = NULL; + + return NULL; +} + +void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) { + unsigned hash; + struct hashmap_entry *e; + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + + e = hash_scan(h, hash, key); + if (!e) + return NULL; + + *i = (Iterator) e; + + return e->value; +} + +void* hashmap_first(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + return h->iterate_list_head->value; +} + +void* hashmap_first_key(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + return (void*) h->iterate_list_head->key; +} + +void* hashmap_last(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_tail) + return NULL; + + return h->iterate_list_tail->value; +} + +void* hashmap_steal_first(Hashmap *h) { + void *data; + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + data = h->iterate_list_head->value; + remove_entry(h, h->iterate_list_head); + + return data; +} + +void* hashmap_steal_first_key(Hashmap *h) { + void *key; + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + key = (void*) h->iterate_list_head->key; + remove_entry(h, h->iterate_list_head); + + return key; +} + +unsigned hashmap_size(Hashmap *h) { + + if (!h) + return 0; + + return h->n_entries; +} + +unsigned hashmap_buckets(Hashmap *h) { + + if (!h) + return 0; + + return h->n_buckets; +} + +bool hashmap_isempty(Hashmap *h) { + + if (!h) + return true; + + return h->n_entries == 0; +} + +int hashmap_merge(Hashmap *h, Hashmap *other) { + struct hashmap_entry *e; + + assert(h); + + if (!other) + return 0; + + for (e = other->iterate_list_head; e; e = e->iterate_next) { + int r; + + if ((r = hashmap_put(h, e->key, e->value)) < 0) + if (r != -EEXIST) + return r; + } + + return 0; +} + +void hashmap_move(Hashmap *h, Hashmap *other) { + struct hashmap_entry *e, *n; + + assert(h); + + /* The same as hashmap_merge(), but every new item from other + * is moved to h. This function is guaranteed to succeed. */ + + if (!other) + return; + + for (e = other->iterate_list_head; e; e = n) { + unsigned h_hash, other_hash; + + n = e->iterate_next; + + h_hash = h->hash_func(e->key) % h->n_buckets; + + if (hash_scan(h, h_hash, e->key)) + continue; + + other_hash = other->hash_func(e->key) % other->n_buckets; + + unlink_entry(other, e, other_hash); + link_entry(h, e, h_hash); + } +} + +int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { + unsigned h_hash, other_hash; + struct hashmap_entry *e; + + if (!other) + return 0; + + assert(h); + + h_hash = h->hash_func(key) % h->n_buckets; + if (hash_scan(h, h_hash, key)) + return -EEXIST; + + other_hash = other->hash_func(key) % other->n_buckets; + e = hash_scan(other, other_hash, key); + if (!e) + return -ENOENT; + + unlink_entry(other, e, other_hash); + link_entry(h, e, h_hash); + + return 0; +} + +Hashmap *hashmap_copy(Hashmap *h) { + Hashmap *copy; + + assert(h); + + copy = hashmap_new(h->hash_func, h->compare_func); + if (!copy) + return NULL; + + if (hashmap_merge(copy, h) < 0) { + hashmap_free(copy); + return NULL; + } + + return copy; +} + +char **hashmap_get_strv(Hashmap *h) { + char **sv; + Iterator it; + char *item; + int n; + + sv = new(char*, h->n_entries+1); + if (!sv) + return NULL; + + n = 0; + HASHMAP_FOREACH(item, h, it) + sv[n++] = item; + sv[n] = NULL; + + return sv; +} + +void *hashmap_next(Hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + + assert(h); + assert(key); + + if (!h) + return NULL; + + hash = h->hash_func(key) % h->n_buckets; + e = hash_scan(h, hash, key); + if (!e) + return NULL; + + e = e->iterate_next; + if (!e) + return NULL; + + return e->value; +} diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h new file mode 100644 index 0000000..d0af8f5 --- /dev/null +++ b/src/shared/hashmap.h @@ -0,0 +1,110 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include "macro.h" + +/* Pretty straightforward hash table implementation. As a minor + * optimization a NULL hashmap object will be treated as empty hashmap + * for all read operations. That way it is not necessary to + * instantiate an object for each Hashmap use. */ + +typedef struct Hashmap Hashmap; +typedef struct _IteratorStruct _IteratorStruct; +typedef _IteratorStruct* Iterator; + +#define ITERATOR_FIRST ((Iterator) 0) +#define ITERATOR_LAST ((Iterator) -1) + +typedef unsigned (*hash_func_t)(const void *p); +typedef int (*compare_func_t)(const void *a, const void *b); + +unsigned string_hash_func(const void *p) _pure_; +int string_compare_func(const void *a, const void *b) _pure_; + +/* This will compare the passed pointers directly, and will not + * dereference them. This is hence not useful for strings or + * suchlike. */ +unsigned trivial_hash_func(const void *p) _const_; +int trivial_compare_func(const void *a, const void *b) _const_; + +unsigned uint64_hash_func(const void *p) _pure_; +int uint64_compare_func(const void *a, const void *b) _pure_; + +Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func); +void hashmap_free(Hashmap *h); +void hashmap_free_free(Hashmap *h); +void hashmap_free_free_free(Hashmap *h); +Hashmap *hashmap_copy(Hashmap *h); +int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func); + +int hashmap_put(Hashmap *h, const void *key, void *value); +int hashmap_update(Hashmap *h, const void *key, void *value); +int hashmap_replace(Hashmap *h, const void *key, void *value); +void *hashmap_get(Hashmap *h, const void *key); +void *hashmap_get2(Hashmap *h, const void *key, void **rkey); +bool hashmap_contains(Hashmap *h, const void *key); +void *hashmap_remove(Hashmap *h, const void *key); +void *hashmap_remove_value(Hashmap *h, const void *key, void *value); +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); + +int hashmap_merge(Hashmap *h, Hashmap *other); +void hashmap_move(Hashmap *h, Hashmap *other); +int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key); + +unsigned hashmap_size(Hashmap *h) _pure_; +bool hashmap_isempty(Hashmap *h) _pure_; +unsigned hashmap_buckets(Hashmap *h) _pure_; + +void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key); +void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key); +void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i); + +void hashmap_clear(Hashmap *h); +void hashmap_clear_free(Hashmap *h); +void hashmap_clear_free_free(Hashmap *h); + +void *hashmap_steal_first(Hashmap *h); +void *hashmap_steal_first_key(Hashmap *h); +void *hashmap_first(Hashmap *h) _pure_; +void *hashmap_first_key(Hashmap *h) _pure_; +void *hashmap_last(Hashmap *h) _pure_; + +void *hashmap_next(Hashmap *h, const void *key); + +char **hashmap_get_strv(Hashmap *h); + +#define HASHMAP_FOREACH(e, h, i) \ + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL)) + +#define HASHMAP_FOREACH_KEY(e, k, h, i) \ + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k))) + +#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \ + for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL)) diff --git a/src/shared/iniparser.c b/src/shared/iniparser.c new file mode 100644 index 0000000..55296af --- /dev/null +++ b/src/shared/iniparser.c @@ -0,0 +1,779 @@ +/* + Copyright (c) 2000-2011 by Nicolas Devillard. + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- Includes ------------------------------------*/ +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#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 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; isize ; 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 ; jsize ; 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 ; jsize ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + keys[i] = d->key[j]; + i++; + } + } + + return keys; + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def) +{ + char * lc_key ; + char * sval ; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return (int)strtol(str, NULL, 0); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, const char * key, double notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound) +{ + char * c ; + int ret ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry( + dictionary * ini, + const char * entry +) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val) +{ + return dictionary_set(ini, strlwc(entry), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry) +{ + dictionary_unset(ini, strlwc(entry)); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + const char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char line[ASCIILINESZ+1]; + int len ; + + strcpy(line, strstrip(input_line)); + len = (int)strlen(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strcpy(section, strstrip(section)); + strcpy(section, strlwc(section)); + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + /* Usual key=value, with or without comments */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + strcpy(value, strstrip(value)); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [ASCIILINESZ+1] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; + len = (int)strlen(line)-1; + if (len==0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n' && !feof(in)) { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + switch (iniparser_line(line, section, key, val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + errs = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + errs = dictionary_set(dict, tmp, val) ; + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + last=0; + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} + +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/src/shared/iniparser.h b/src/shared/iniparser.h new file mode 100644 index 0000000..62d6de9 --- /dev/null +++ b/src/shared/iniparser.h @@ -0,0 +1,330 @@ +/* + Copyright (c) 2000-2011 by Nicolas Devillard. + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#pragma once + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.h + @author N. Devillard + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include */ + +#include "dictionary.h" + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ + +int iniparser_getnsec(dictionary * d); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ + +char * iniparser_getsecname(dictionary * d, int n); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dump_ini(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(dictionary * d, char * s); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return pointer to statically allocated character strings + + This function queries a dictionary and finds all keys in a given section. + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char ** iniparser_getseckeys(dictionary * d, char * s); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, const char * key, double notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry); + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(dictionary * ini, const char * entry) ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame); + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d); diff --git a/src/shared/list.h b/src/shared/list.h new file mode 100644 index 0000000..4a6843d --- /dev/null +++ b/src/shared/list.h @@ -0,0 +1,136 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define LIST_HEAD(t,name) \ + t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define LIST_FIELDS(t,name) \ + t *name##_next, *name##_prev + +/* Initialize the list's head */ +#define LIST_HEAD_INIT(t,head) \ + do { \ + (head) = NULL; } \ + while(false) + +/* Initialize a list item */ +#define LIST_INIT(t,name,item) \ + do { \ + t *_item = (item); \ + assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while(false) + +/* Prepend an item to the list */ +#define LIST_PREPEND(t,name,head,item) \ + do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while(false) + +/* Remove an item from the list */ +#define LIST_REMOVE(t,name,head,item) \ + do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else { \ + assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while(false) + +/* Find the head of the list */ +#define LIST_FIND_HEAD(t,name,item,head) \ + do { \ + t *_item = (item); \ + assert(_item); \ + while (_item->name##_prev) \ + _item = _item->name##_prev; \ + (head) = _item; \ + } while (false) + +/* Find the tail of the list */ +#define LIST_FIND_TAIL(t,name,item,tail) \ + do { \ + t *_item = (item); \ + assert(_item); \ + while (_item->name##_next) \ + _item = _item->name##_next; \ + (tail) = _item; \ + } while (false) + +/* Insert an item after another one (a = where, b = what) */ +#define LIST_INSERT_AFTER(t,name,head,a,b) \ + do { \ + t **_head = &(head), *_a = (a), *_b = (b); \ + assert(_b); \ + if (!_a) { \ + if ((_b->name##_next = *_head)) \ + _b->name##_next->name##_prev = _b; \ + _b->name##_prev = NULL; \ + *_head = _b; \ + } else { \ + if ((_b->name##_next = _a->name##_next)) \ + _b->name##_next->name##_prev = _b; \ + _b->name##_prev = _a; \ + _a->name##_next = _b; \ + } \ + } while(false) + +#define LIST_JUST_US(name,item) \ + (!(item)->name##_prev && !(item)->name##_next) \ + +#define LIST_FOREACH(name,i,head) \ + for ((i) = (head); (i); (i) = (i)->name##_next) + +#define LIST_FOREACH_SAFE(name,i,n,head) \ + for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) + +#define LIST_FOREACH_BEFORE(name,i,p) \ + for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) + +#define LIST_FOREACH_AFTER(name,i,p) \ + for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) + +/* Loop starting from p->next until p->prev. + p can be adjusted meanwhile. */ +#define LIST_LOOP_BUT_ONE(name,i,head,p) \ + for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ + (i) != (p); \ + (i) = (i)->name##_next ? (i)->name##_next : (head)) diff --git a/src/shared/log.c b/src/shared/log.c new file mode 100644 index 0000000..d3ae03c --- /dev/null +++ b/src/shared/log.c @@ -0,0 +1,39 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +void buxton_log(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/log.h b/src/shared/log.h new file mode 100644 index 0000000..080acdb --- /dev/null +++ b/src/shared/log.h @@ -0,0 +1,39 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#ifdef DEBUG +#define buxton_debug(...) do { \ + (buxton_log("%s():[%d]: ", __func__, __LINE__), buxton_log(__VA_ARGS__)); \ +} while(0); +#else +#define buxton_debug(...) do {} while(0); +#endif /* DEBUG */ + +void buxton_log(const char *fmt, ...); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/macro.h b/src/shared/macro.h new file mode 100644 index 0000000..c4abb0b --- /dev/null +++ b/src/shared/macro.h @@ -0,0 +1,83 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include + +#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b))) +#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) +#define _sentinel_ __attribute__ ((sentinel)) +#define _noreturn_ __attribute__((noreturn)) +#define _unused_ __attribute__ ((unused)) +#define _destructor_ __attribute__ ((destructor)) +#define _pure_ __attribute__ ((pure)) +#define _const_ __attribute__ ((const)) +#define _deprecated_ __attribute__ ((deprecated)) +#define _packed_ __attribute__ ((packed)) +#define _malloc_ __attribute__ ((malloc)) +#define _weak_ __attribute__ ((weak)) +#define _likely_(x) (__builtin_expect(!!(x),1)) +#define _unlikely_(x) (__builtin_expect(!!(x),0)) +#define _public_ __attribute__ ((visibility("default"))) +#define _hidden_ __attribute__ ((visibility("hidden"))) +#define _weakref_(x) __attribute__((weakref(#x))) +#define _introspect_(x) __attribute__((section("introspect." x))) +#define _alignas_(x) __attribute__((aligned(__alignof(x)))) +#define _cleanup_(x) __attribute__((cleanup(x))) + +/* Rounds up */ + +#define ALIGN4(l) (((l) + 3) & (unsigned)~3) +#define ALIGN8(l) (((l) + 7) & (unsigned)~7) + +#if __SIZEOF_POINTER__ == 8 +#define ALIGN(l) ALIGN8(l) +#elif __SIZEOF_POINTER__ == 4 +#define ALIGN(l) ALIGN4(l) +#else +#error "Wut? Pointers are neither 4 nor 8 bytes long?" +#endif + +#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p)) +#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p)) +#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p)) + +static inline size_t ALIGN_TO(size_t l, size_t ali) { + return ((l + ali - 1) & (unsigned)~(ali - 1)); +} + +#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p)) + +#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) +#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define memzero(x,l) (memset((x), 0, (l))) diff --git a/src/shared/protocol.c b/src/shared/protocol.c new file mode 100644 index 0000000..fdf73c4 --- /dev/null +++ b/src/shared/protocol.c @@ -0,0 +1,937 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "buxtonclient.h" +#include "buxtonkey.h" +#include "buxtonresponse.h" +#include "buxtonstring.h" +#include "hashmap.h" +#include "log.h" +#include "protocol.h" +#include "util.h" + +#define TIMEOUT 3 + +static pthread_mutex_t callback_guard = PTHREAD_MUTEX_INITIALIZER; +static Hashmap *callbacks = NULL; +static Hashmap *notify_callbacks = NULL; +static volatile uint32_t _msgid = 0; + +struct notify_value { + void *data; + BuxtonCallback cb; + struct timeval tv; + BuxtonControlMessage type; + _BuxtonKey *key; +}; + +static uint32_t get_msgid(void) +{ + return __sync_fetch_and_add(&_msgid, 1); +} + +bool setup_callbacks(void) +{ + bool r = false; + int s; + + s = pthread_mutex_lock(&callback_guard); + if (s) { + return false; + } + + if (callbacks && notify_callbacks) { + goto unlock; + } + + callbacks = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!callbacks) { + goto unlock; + } + + notify_callbacks = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!notify_callbacks) { + hashmap_free(callbacks); + goto unlock; + } + + r = true; + +unlock: + s = pthread_mutex_unlock(&callback_guard); + if (s) { + return false; + } + + return r; +} + +void cleanup_callbacks(void) +{ + struct notify_value *nvi; + Iterator it; +#if UINTPTR_MAX == 0xffffffffffffffff + uint64_t hkey; +#else + uint32_t hkey; +#endif + + (void)pthread_mutex_lock(&callback_guard); + + if (callbacks) { + HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) { + (void)hashmap_remove(callbacks, (void *)hkey); + key_free(nvi->key); + free(nvi); + } + hashmap_free(callbacks); + } + callbacks = NULL; + + if (notify_callbacks) { + HASHMAP_FOREACH_KEY(nvi, hkey, notify_callbacks, it) { + (void)hashmap_remove(notify_callbacks, (void *)hkey); + key_free(nvi->key); + free(nvi); + } + hashmap_free(notify_callbacks); + } + notify_callbacks = NULL; + + (void)pthread_mutex_unlock(&callback_guard); +} + +void run_callback(BuxtonCallback callback, void *data, size_t count, + BuxtonData *list, BuxtonControlMessage type, _BuxtonKey *key) +{ + BuxtonArray *array = NULL; + _BuxtonResponse response; + + if (!callback) { + goto out; + } + + array = buxton_array_new(); + if (!array) { + goto out; + } + + for (int i = 0; i < count; i++) + if (!buxton_array_add(array, &list[i])) { + goto out; + } + + response.type = type; + response.data = array; + response.key = key; + callback(&response, data); + +out: + buxton_array_free(&array, NULL); +} + +void reap_callbacks(void) +{ + struct notify_value *nvi; + struct timeval tv; + Iterator it; +#if UINTPTR_MAX == 0xffffffffffffffff + uint64_t hkey; +#else + uint32_t hkey; +#endif + + (void)gettimeofday(&tv, NULL); + + /* remove timed out callbacks */ + HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) { + if (tv.tv_sec - nvi->tv.tv_sec > TIMEOUT) { + (void)hashmap_remove(callbacks, (void *)hkey); + key_free(nvi->key); + free(nvi); + } + } +} + +bool send_message(_BuxtonClient *client, uint8_t *send, size_t send_len, + BuxtonCallback callback, void *data, uint32_t msgid, + BuxtonControlMessage type, _BuxtonKey *key) +{ + struct notify_value *nv; + _BuxtonKey *k = NULL; + int s; + bool r = false; + + nv = malloc0(sizeof(struct notify_value)); + if (!nv) { + goto fail; + } + + if (key) { + k = malloc0(sizeof(_BuxtonKey)); + if (!k) { + goto fail; + } + if (!buxton_key_copy(key, k)) { + goto fail; + } + } + + (void)gettimeofday(&nv->tv, NULL); + nv->cb = callback; + nv->data = data; + nv->type = type; + nv->key = k; + + s = pthread_mutex_lock(&callback_guard); + if (s) { + goto fail; + } + + reap_callbacks(); + +#if UINTPTR_MAX == 0xffffffffffffffff + s = hashmap_put(callbacks, (void *)((uint64_t)msgid), nv); +#else + s = hashmap_put(callbacks, (void *)msgid, nv); +#endif + (void)pthread_mutex_unlock(&callback_guard); + + if (s < 1) { + buxton_debug("Error adding callback for msgid: %llu\n", msgid); + goto fail; + } + + /* Now write it off */ + if (!_write(client->fd, send, send_len)) { + buxton_debug("Write failed for msgid: %llu\n", msgid); + r = false; + } else { + r = true; + } + + return r; + +fail: + free(nv); + key_free(k); + return false; +} + +void handle_callback_response(BuxtonControlMessage msg, uint32_t msgid, + BuxtonData *list, size_t count) +{ + struct notify_value *nv; + + /* use notification callbacks for notification messages */ + if (msg == BUXTON_CONTROL_CHANGED) { +#if UINTPTR_MAX == 0xffffffffffffffff + nv = hashmap_get(notify_callbacks, (void *)((uint64_t)msgid)); +#else + nv = hashmap_get(notify_callbacks, (void *)msgid); +#endif + if (!nv) { + return; + } + + run_callback((BuxtonCallback)(nv->cb), nv->data, count, list, + BUXTON_CONTROL_CHANGED, nv->key); + return; + } + +#if UINTPTR_MAX == 0xffffffffffffffff + nv = hashmap_remove(callbacks, (void *)((uint64_t)msgid)); +#else + nv = hashmap_remove(callbacks, (void *)msgid); +#endif + if (!nv) { + return; + } + + if (nv->type == BUXTON_CONTROL_NOTIFY) { + if (list[0].type == INT32 && + list[0].store.d_int32 == 0) { +#if UINTPTR_MAX == 0xffffffffffffffff + if (hashmap_put(notify_callbacks, (void *)((uint64_t)msgid), nv) +#else + if (hashmap_put(notify_callbacks, (void *)msgid, nv) +#endif + >= 0) { + return; + } + } + } else if (nv->type == BUXTON_CONTROL_UNNOTIFY) { + if (list[0].type == INT32 && + list[0].store.d_int32 == 0) { + (void)hashmap_remove(notify_callbacks, +#if UINTPTR_MAX == 0xffffffffffffffff + (void *)((uint64_t)list[2].store.d_uint32)); +#else + (void *)list[2].store.d_uint32); +#endif + + return; + } + } + + /* callback should be run on notfiy or unnotify failure */ + /* and on any other server message we are waiting for */ + run_callback((BuxtonCallback)(nv->cb), nv->data, count, list, nv->type, + nv->key); + + key_free(nv->key); + free(nv); +} + +ssize_t buxton_wire_handle_response(_BuxtonClient *client) +{ + ssize_t l; + _cleanup_free_ uint8_t *response = NULL; + BuxtonData *r_list = NULL; + BuxtonControlMessage r_msg = BUXTON_CONTROL_MIN; + ssize_t count = 0; + size_t offset = 0; + size_t size = BUXTON_MESSAGE_HEADER_LENGTH; + uint32_t r_msgid; + int s; + ssize_t handled = 0; + + s = pthread_mutex_lock(&callback_guard); + if (s) { + return 0; + } + reap_callbacks(); + (void)pthread_mutex_unlock(&callback_guard); + + response = malloc0(BUXTON_MESSAGE_HEADER_LENGTH); + if (!response) { + return 0; + } + + do { + l = read(client->fd, response + offset, size - offset); + if (l <= 0) { + return handled; + } + offset += (size_t)l; + if (offset < BUXTON_MESSAGE_HEADER_LENGTH) { + continue; + } + if (size == BUXTON_MESSAGE_HEADER_LENGTH) { + size = buxton_get_message_size(response, offset); + if (size == 0 || size > BUXTON_MESSAGE_MAX_LENGTH) { + return -1; + } + } + if (size != BUXTON_MESSAGE_HEADER_LENGTH) { + response = realloc(response, size); + if (!response) { + return -1; + } + } + if (size != offset) { + continue; + } + + count = buxton_deserialize_message(response, &r_msg, size, &r_msgid, &r_list); + if (count < 0) { + goto next; + } + + if (!(r_msg == BUXTON_CONTROL_STATUS && r_list && r_list[0].type == INT32) + && !(r_msg == BUXTON_CONTROL_CHANGED)) { + handled++; + buxton_log("Critical error: Invalid response\n"); + goto next; + } + + s = pthread_mutex_lock(&callback_guard); + if (s) { + goto next; + } + + handle_callback_response(r_msg, r_msgid, r_list, (size_t)count); + + (void)pthread_mutex_unlock(&callback_guard); + handled++; + + next: + if (r_list) { + for (int i = 0; i < count; i++) { + if (r_list[i].type == STRING) { + free(r_list[i].store.d_string.value); + } + } + free(r_list); + } + + /* reset for next possible message */ + size = BUXTON_MESSAGE_HEADER_LENGTH; + offset = 0; + } while (true); +} + +int buxton_wire_get_response(_BuxtonClient *client) +{ + struct pollfd pfd[1]; + int r; + ssize_t processed; + + pfd[0].fd = client->fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r == 0) { + return -ETIME; + } else if (r < 0) { + return -errno; + } + + processed = buxton_wire_handle_response(client); + + return (int)processed; +} + +bool buxton_wire_set_value(_BuxtonClient *client, _BuxtonKey *key, void *value, + BuxtonCallback callback, void *data) +{ + _cleanup_free_ uint8_t *send = NULL; + bool ret = false; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_value; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->layer, &d_layer); + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + d_value.type = key->type; + switch (key->type) { + case STRING: + d_value.store.d_string.value = (char *)value; + d_value.store.d_string.length = (uint32_t)strlen((char *)value) + 1; + break; + case INT32: + d_value.store.d_int32 = *(int32_t *)value; + break; + case INT64: + d_value.store.d_int64 = *(int64_t *)value; + break; + case UINT32: + d_value.store.d_uint32 = *(uint32_t *)value; + break; + case UINT64: + d_value.store.d_uint64 = *(uint64_t *)value; + break; + case FLOAT: + d_value.store.d_float = *(float *)value; + break; + case DOUBLE: + memcpy(&d_value.store.d_double, value, sizeof(double)); + break; + case BOOLEAN: + d_value.store.d_boolean = *(bool *)value; + break; + default: + break; + } + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Failed to add layer to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_value)) { + buxton_log("Failed to add value to set_value array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET, msgid, list); + + if (send_len == 0) { + goto end; + } + + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_SET, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_set_label(_BuxtonClient *client, + _BuxtonKey *key, BuxtonString *value, + BuxtonCallback callback, void *data) +{ + assert(client); + assert(key); + assert(value); + + _cleanup_free_ uint8_t *send = NULL; + bool ret = false; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_value; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->layer, &d_layer); + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(value, &d_value); + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Failed to add layer to set_label array\n"); + goto end; + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_label array\n"); + goto end; + } + if (key->name.value) { + buxton_string_to_data(&key->name, &d_name); + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_label array\n"); + goto end; + } + } + if (!buxton_array_add(list, &d_value)) { + buxton_log("Failed to add value to set_label array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET_LABEL, msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_SET_LABEL, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_create_group(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) +{ + assert(client); + assert(key); + + _cleanup_free_ uint8_t *send = NULL; + bool ret = false; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->layer, &d_layer); + buxton_string_to_data(&key->group, &d_group); + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Failed to add layer to create_group array\n"); + goto end; + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to create_group array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_CREATE_GROUP, msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_CREATE_GROUP, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) +{ + assert(client); + assert(key); + + _cleanup_free_ uint8_t *send = NULL; + bool ret = false; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->layer, &d_layer); + buxton_string_to_data(&key->group, &d_group); + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Failed to add layer to remove_group array\n"); + goto end; + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to remove_group array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_REMOVE_GROUP, msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_REMOVE_GROUP, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) +{ + bool ret = false; + size_t send_len = 0; + _cleanup_free_ uint8_t *send = NULL; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_type; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + d_type.type = UINT32; + d_type.store.d_int32 = key->type; + + list = buxton_array_new(); + if (key->layer.value) { + buxton_string_to_data(&key->layer, &d_layer); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Unable to prepare get_value message\n"); + goto end; + } + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_type)) { + buxton_log("Failed to add type to set_value array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_GET, msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_GET, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_unset_value(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) +{ + assert(client); + assert(key); + + _cleanup_free_ uint8_t *send = NULL; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_layer; + BuxtonData d_type; + bool ret = false; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + buxton_string_to_data(&key->layer, &d_layer); + d_type.type = UINT32; + d_type.store.d_int32 = key->type; + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Failed to add layer to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_type)) { + buxton_log("Failed to add type to set_value array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNSET, + msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_UNSET, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_list_keys(_BuxtonClient *client, + BuxtonString *layer, + BuxtonCallback callback, + void *data) +{ + assert(client); + assert(layer); + + _cleanup_free_ uint8_t *send = NULL; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_layer; + bool ret = false; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(layer, &d_layer); + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Unable to add layer to list_keys array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_LIST, msgid, + list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_LIST, NULL)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + + return ret; +} + +bool buxton_wire_register_notification(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) +{ + assert(client); + assert(key); + + _cleanup_free_ uint8_t *send = NULL; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_type; + bool ret = false; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + d_type.type = UINT32; + d_type.store.d_int32 = key->type; + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_type)) { + buxton_log("Failed to add type to set_value array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_NOTIFY, msgid, + list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_NOTIFY, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +bool buxton_wire_unregister_notification(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) +{ + assert(client); + assert(key); + + _cleanup_free_ uint8_t *send = NULL; + size_t send_len = 0; + BuxtonArray *list = NULL; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_type; + bool ret = false; + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + d_type.type = UINT32; + d_type.store.d_int32 = key->type; + + list = buxton_array_new(); + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to set_value array\n"); + goto end; + } + if (!buxton_array_add(list, &d_type)) { + buxton_log("Failed to add type to set_value array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNNOTIFY, + msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_UNNOTIFY, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + +void include_protocol(void) +{ + ; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/protocol.h b/src/shared/protocol.h new file mode 100644 index 0000000..b3e789e --- /dev/null +++ b/src/shared/protocol.h @@ -0,0 +1,244 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file protocol.h Internal header + * This file is used internally by buxton to provide functionality + * used for the wire protocol + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "buxton.h" +#include "buxtonclient.h" +#include "buxtonkey.h" +#include "list.h" +#include "serialize.h" +#include "hashmap.h" + +/** + * Initialize callback hashamps + * @return a boolean value, indicating success of the operation + */ +bool setup_callbacks(void) + __attribute__((warn_unused_result)); + +/** + * free callback hashmaps + */ +void cleanup_callbacks(void); + +/** + * Execute callback function on list using user data + * @param callback User callback function executed + * @param data User data passed to callback function + * @param count number of elements in list + * @param list Data from buxtond + * @param type Message type of the callback + * @param key Key used to make the request + */ +void run_callback(BuxtonCallback callback, void *data, size_t count, + BuxtonData *list, BuxtonControlMessage type, + _BuxtonKey *key); + +/** + * cleanup expired messages (must hold callback_guard lock) + */ +void reap_callbacks(void); + +/** + * Write message to buxtond + * @param client Client connection + * @param send serialized data to send to buxtond + * @param send_len size of send + * @param callback Callback function used to handle buxtond's response + * @param data User data passed to callback function + * @param msgid Message id used to identify buxtond's response + * @param type The type of request being sent to buxtond + * @return a boolean value, indicating success of the operation + */ +bool send_message(_BuxtonClient *client, uint8_t *send, size_t send_len, + BuxtonCallback callback, void *data, uint32_t msgid, + BuxtonControlMessage type, _BuxtonKey *key) + __attribute__((warn_unused_result)); + +/** + * Check for callbacks for daemon's response + * @param msg Buxton message type + * @param msgid Key for message lookup + * @param list array of BuxtonData + * @param count number of elements in list + */ +void handle_callback_response(BuxtonControlMessage msg, uint32_t msgid, + BuxtonData *list, size_t count); + +/** + * Parse responses from buxtond and run callbacks on received messages + * @param client A BuxtonClient + * @return number of received messages processed + */ + +ssize_t buxton_wire_handle_response(_BuxtonClient *client) + __attribute__((warn_unused_result)); + +/** + * Wait for a response from buxtond and then call handle response + * @param client Client connection + * @return an int value, indicating success of the operation + */ +int buxton_wire_get_response(_BuxtonClient *client); + +/** + * Send a SET message over the wire protocol, return the response + * @param client Client connection + * @param key _BuxtonKey pointer + * @param value A pointer to a new value + * @param data A BuxtonData storing the new value + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_set_value(_BuxtonClient *client, _BuxtonKey *key, void *value, + BuxtonCallback callback, void *data) + __attribute__((warn_unused_result)); + +/** + * Send a SET_LABEL message over the wire protocol, return the response + * + * @note This is a privileged operation, so it will return false for unprivileged clients + * + * @param client Client connection + * @param key Key or group name + * @param value Key or group label + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_set_label(_BuxtonClient *client, _BuxtonKey *key, + BuxtonString *value, BuxtonCallback callback, + void *data) + __attribute__((warn_unused_result)); + +/** + * Send a CREATE_GROUP message over the wire protocol, return the response + * + * @note This is a privileged operation, so it will return false for unprivileged clients + * + * @param client Client connection + * @param key Key with group and layer members initialized + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_create_group(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) + __attribute__((warn_unused_result)); + +/** + * Send a REMOVE_GROUP message over the wire protocol, return the response + * + * @note This is a privileged operation, so it will return false for unprivileged clients + * + * @param client Client connection + * @param key Key with group and layer members initialized + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) + __attribute__((warn_unused_result)); + +/** + * Send a GET message over the wire protocol, return the data + * @param client Client connection + * @param key _BuxtonKey pointer + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback functionb + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) + __attribute__((warn_unused_result)); + + +/** + * Send an UNSET message over the wire protocol, return the response + * @param client Client connection + * @param key _BuxtonKey pointer + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_unset_value(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) + __attribute__((warn_unused_result)); +/** + * Send a NOTIFY message over the protocol, register for events + * @param client Client connection + * @param key _BuxtonKey pointer + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_register_notification(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) + __attribute__((warn_unused_result)); + +/** + * Send a LIST message over the protocol, retrieve key list + * @param client Client connection + * @param layer Layer name + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_list_keys(_BuxtonClient *client, + BuxtonString *layer, + BuxtonCallback callback, + void *data) + __attribute__((warn_unused_result)); + +/** + * Send an UNNOTIFY message over the protocol, no longer recieve events + * @param client Client connection + * @param key _BuxtonKey pointer + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback function + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_unregister_notification(_BuxtonClient *client, + _BuxtonKey *key, + BuxtonCallback callback, + void *data) + __attribute__((warn_unused_result)); + +void include_protocol(void); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/serialize.c b/src/shared/serialize.c new file mode 100644 index 0000000..86d3a7d --- /dev/null +++ b/src/shared/serialize.c @@ -0,0 +1,570 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "log.h" +#include "serialize.h" +#include "smack.h" +#include "util.h" + + +size_t buxton_serialize(BuxtonData *source, BuxtonString *label, + uint8_t **target) +{ + size_t length; + size_t size; + size_t offset = 0; + uint8_t *data = NULL; + size_t ret = 0; + + assert(source); + assert(target); + + /* DataType + length field */ + size = sizeof(BuxtonDataType) + (sizeof(uint32_t) * 2) + label->length; + + /* Total size will be different for string data */ + switch (source->type) { + case STRING: + length = source->store.d_string.length; + break; + default: + length = sizeof(source->store); + break; + } + + size += length; + + /* Allocate memory big enough to hold all information */ + data = malloc0(size); + if (!data) { + abort(); + } + + /* Write the entire BuxtonDataType to the first block */ + memcpy(data, &(source->type), sizeof(BuxtonDataType)); + offset += sizeof(BuxtonDataType); + + /* Write out the length of the label field */ + memcpy(data+offset, &(label->length), sizeof(uint32_t)); + offset += sizeof(uint32_t); + + /* Write out the length of the data field */ + memcpy(data+offset, &length, sizeof(uint32_t)); + offset += sizeof(uint32_t); + + /* Write out the label field */ + memcpy(data+offset, label->value, label->length); + offset += label->length; + + /* Write the data itself */ + switch (source->type) { + case STRING: + memcpy(data+offset, source->store.d_string.value, length); + break; + case INT32: + memcpy(data+offset, &(source->store.d_int32), sizeof(int32_t)); + break; + case UINT32: + memcpy(data+offset, &(source->store.d_uint32), sizeof(uint32_t)); + break; + case INT64: + memcpy(data+offset, &(source->store.d_int64), sizeof(int64_t)); + break; + case UINT64: + memcpy(data+offset, &(source->store.d_uint64), sizeof(uint64_t)); + break; + case FLOAT: + memcpy(data+offset, &(source->store.d_float), sizeof(float)); + break; + case DOUBLE: + memcpy(data+offset, &(source->store.d_double), sizeof(double)); + break; + case BOOLEAN: + memcpy(data+offset, &(source->store.d_boolean), sizeof(bool)); + break; + default: + abort(); + } + + ret = size; + *target = data; + + assert(ret >= BXT_MINIMUM_SIZE); + + return ret; +} + +void buxton_deserialize(uint8_t *source, BuxtonData *target, + BuxtonString *label) +{ + size_t offset = 0; + size_t length = 0; + BuxtonDataType type; + + assert(source); + assert(target); + assert(label); + + /* Retrieve the BuxtonDataType */ + type = *(BuxtonDataType*)source; + offset += sizeof(BuxtonDataType); + + /* Retrieve the length of the label */ + label->length = *(uint32_t*)(source+offset); + offset += sizeof(uint32_t); + + /* Retrieve the length of the value */ + length = *(uint32_t*)(source+offset); + offset += sizeof(uint32_t); + + /* Retrieve the label */ + label->value = malloc(label->length); + if (label->length > 0 && !label->value) { + abort(); + } + memcpy(label->value, source+offset, label->length); + offset += label->length; + + switch (type) { + case STRING: + /* User must free the string */ + target->store.d_string.value = malloc(length); + if (!target->store.d_string.value) { + abort(); + } + memcpy(target->store.d_string.value, source+offset, length); + target->store.d_string.length = (uint32_t)length; + break; + case INT32: + target->store.d_int32 = *(int32_t*)(source+offset); + break; + case UINT32: + target->store.d_uint32 = *(uint32_t*)(source+offset); + break; + case INT64: + target->store.d_int64 = *(int64_t*)(source+offset); + break; + case UINT64: + target->store.d_uint64 = *(uint64_t*)(source+offset); + break; + case FLOAT: + target->store.d_float = *(float*)(source+offset); + break; + case DOUBLE: + memcpy(&target->store.d_double, source + offset, sizeof(double)); + break; + case BOOLEAN: + target->store.d_boolean = *(bool*)(source+offset); + break; + default: + buxton_debug("Invalid BuxtonDataType: %lu\n", type); + abort(); + } + + /* Successful */ + target->type = type; +} + +size_t buxton_serialize_message(uint8_t **dest, BuxtonControlMessage message, + uint32_t msgid, BuxtonArray *list) +{ + uint16_t i = 0; + uint8_t *data = NULL; + size_t ret = 0; + size_t offset = 0; + size_t size = 0; + size_t curSize = 0; + uint16_t control, msg; + + assert(dest); + + buxton_debug("Serializing message...\n"); + + if (list->len > BUXTON_MESSAGE_MAX_PARAMS) { + errno = EINVAL; + return ret; + } + + if (message >= BUXTON_CONTROL_MAX || message < BUXTON_CONTROL_SET) { + errno = EINVAL; + return ret; + } + + /* + * initial size = + * control code + control message (uint16_t * 2) + + * message size (uint32_t) + + * message id (uint32_t) + + * param count (uint32_t) + */ + data = malloc0(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + + sizeof(uint32_t)); + if (!data) { + errno = ENOMEM; + goto end; + } + + control = BUXTON_CONTROL_CODE; + memcpy(data, &control, sizeof(uint16_t)); + offset += sizeof(uint16_t); + + msg = (uint16_t)message; + memcpy(data+offset, &msg, sizeof(uint16_t)); + offset += sizeof(uint16_t); + + /* Save room for final size */ + offset += sizeof(uint32_t); + + memcpy(data+offset, &msgid, sizeof(uint32_t)); + offset += sizeof(uint32_t); + + /* Now write the parameter count */ + memcpy(data+offset, &(list->len), sizeof(uint32_t)); + offset += sizeof(uint32_t); + + size = offset; + + /* Deal with parameters */ + BuxtonData *param; + size_t p_length = 0; + for (i=0; i < list->len; i++) { + param = buxton_array_get(list, i); + if (!param) { + errno = EINVAL; + goto fail; + } + + switch (param->type) { + case STRING: + p_length = param->store.d_string.length; + break; + case INT32: + p_length = sizeof(int32_t); + break; + case UINT32: + p_length = sizeof(uint32_t); + break; + case INT64: + p_length = sizeof(int64_t); + break; + case UINT64: + p_length = sizeof(uint64_t); + break; + case FLOAT: + p_length = sizeof(float); + break; + case DOUBLE: + p_length = sizeof(double); + break; + case BOOLEAN: + p_length = sizeof(bool); + break; + default: + errno = EINVAL; + buxton_log("Invalid parameter type %lu\n", param->type); + goto fail; + }; + + buxton_debug("offset: %lu\n", offset); + buxton_debug("value length: %lu\n", p_length); + + /* Need to allocate enough room to hold this data */ + size += sizeof(uint16_t) + sizeof(uint32_t) + p_length; + + if (curSize < size) { + if (!(data = greedy_realloc((void**)&data, &curSize, size))) { + errno = ENOMEM; + goto fail; + } + memzero(data+offset, size - offset); + } + + /* Copy data type */ + memcpy(data+offset, &(param->type), sizeof(uint16_t)); + offset += sizeof(uint16_t); + + /* Write out the length of value */ + memcpy(data+offset, &p_length, sizeof(uint32_t)); + offset += sizeof(uint32_t); + + switch (param->type) { + case STRING: + memcpy(data+offset, param->store.d_string.value, p_length); + break; + case INT32: + memcpy(data+offset, &(param->store.d_int32), sizeof(int32_t)); + break; + case UINT32: + memcpy(data+offset, &(param->store.d_uint32), sizeof(uint32_t)); + break; + case INT64: + memcpy(data+offset, &(param->store.d_int64), sizeof(int64_t)); + break; + case UINT64: + memcpy(data+offset, &(param->store.d_uint64), sizeof(uint64_t)); + break; + case FLOAT: + memcpy(data+offset, &(param->store.d_float), sizeof(float)); + break; + case DOUBLE: + memcpy(data+offset, &(param->store.d_double), sizeof(double)); + break; + case BOOLEAN: + memcpy(data+offset, &(param->store.d_boolean), sizeof(bool)); + break; + default: + /* already tested this above, can't get here + * normally */ + assert(0); + }; + offset += p_length; + p_length = 0; + } + + memcpy(data+BUXTON_LENGTH_OFFSET, &offset, sizeof(uint32_t)); + + ret = offset; + *dest = data; + +fail: + /* Clean up */ + if (ret == 0) { + free(data); + } +end: + buxton_debug("Serializing returned:%lu\n", ret); + return ret; +} + +ssize_t buxton_deserialize_message(uint8_t *data, + BuxtonControlMessage *r_message, + size_t size, uint32_t *r_msgid, + BuxtonData **list) +{ + size_t offset = 0; + ssize_t ret = -1; + size_t min_length = BUXTON_MESSAGE_HEADER_LENGTH; + uint16_t control, message; + size_t n_params, c_param, c_length; + BuxtonDataType c_type = 0; + BuxtonData *k_list = NULL; + BuxtonData c_data; + uint32_t msgid; + + assert(data); + assert(r_message); + assert(list); + + buxton_debug("Deserializing message...\n"); + buxton_debug("size=%lu\n", size); + + if (size < min_length) { + errno = EINVAL; + goto end; + } + + /* Copy the control code */ + control = *(uint16_t*)data; + offset += sizeof(uint16_t); + + /* Check this is a valid buxton message */ + if (control != BUXTON_CONTROL_CODE) { + errno = EINVAL; + goto end; + } + + /* Obtain the control message */ + message = *(BuxtonControlMessage*)(data+offset); + offset += sizeof(uint16_t); + + /* Ensure control message is in valid range */ + if (message <= BUXTON_CONTROL_MIN || message >= BUXTON_CONTROL_MAX) { + errno = EINVAL; + goto end; + } + + /* Skip size since our caller got this already */ + offset += sizeof(uint32_t); + + /* Obtain the message id */ + msgid = *(uint32_t*)(data+offset); + offset += sizeof(uint32_t); + + /* Obtain number of parameters */ + n_params = *(uint32_t*)(data+offset); + offset += sizeof(uint32_t); + buxton_debug("total params: %d\n", n_params); + + if (n_params > BUXTON_MESSAGE_MAX_PARAMS) { + errno = EINVAL; + goto end; + } + + k_list = malloc0(sizeof(BuxtonData)*n_params); + if (n_params && !k_list) { + errno = ENOMEM; + goto end; + } + + memzero(&c_data, sizeof(BuxtonData)); + + for (c_param = 0; c_param < n_params; c_param++) { + buxton_debug("param: %d\n", c_param + 1); + buxton_debug("offset=%lu\n", offset); + /* Don't read past the end of the buffer */ + if (offset + sizeof(uint16_t) + sizeof(uint32_t) > size) { + errno = EINVAL; + goto end; + } + + /* Now unpack type */ + memcpy(&c_type, data+offset, sizeof(uint16_t)); + offset += sizeof(uint16_t); + + if (c_type >= BUXTON_TYPE_MAX || c_type <= BUXTON_TYPE_MIN) { + errno = EINVAL; + goto end; + } + + /* Retrieve the length of the value */ + c_length = *(uint32_t*)(data+offset); + if (c_length == 0 && c_type != STRING) { + errno = EINVAL; + goto end; + } + offset += sizeof(uint32_t); + buxton_debug("value length: %lu\n", c_length); + + /* Don't try to read past the end of our buffer */ + if (offset + c_length > size) { + errno = EINVAL; + goto end; + } + + switch (c_type) { + case STRING: + if (c_length) { + c_data.store.d_string.value = malloc(c_length); + if (!c_data.store.d_string.value) { + errno = ENOMEM; + goto end; + } + memcpy(c_data.store.d_string.value, data+offset, c_length); + c_data.store.d_string.length = (uint32_t)c_length; + if (c_data.store.d_string.value[c_length-1] != 0x00) { + errno = EINVAL; + buxton_debug("buxton_deserialize_message(): Garbage message\n"); + free(c_data.store.d_string.value); + goto end; + } + } else { + c_data.store.d_string.value = NULL; + c_data.store.d_string.length = 0; + } + break; + case INT32: + c_data.store.d_int32 = *(int32_t*)(data+offset); + break; + case UINT32: + c_data.store.d_uint32 = *(uint32_t*)(data+offset); + break; + case INT64: + c_data.store.d_int64 = *(int64_t*)(data+offset); + break; + case UINT64: + c_data.store.d_uint64 = *(uint64_t*)(data+offset); + break; + case FLOAT: + c_data.store.d_float = *(float*)(data+offset); + break; + case DOUBLE: + memcpy(&c_data.store.d_double, data + offset, sizeof(double)); + break; + case BOOLEAN: + c_data.store.d_boolean = *(bool*)(data+offset); + break; + default: + errno = EINVAL; + goto end; + } + c_data.type = c_type; + k_list[c_param] = c_data; + memzero(&c_data, sizeof(BuxtonData)); + offset += c_length; + } + *r_message = message; + *r_msgid = msgid; + if (n_params == 0) { + *list = NULL; + free(k_list); + k_list = NULL; + } else { + *list = k_list; + } + ret = (ssize_t)n_params; +end: + if (ret <= 0) { + free(k_list); + } + + buxton_debug("Deserializing returned:%i\n", ret); + return ret; +} + +size_t buxton_get_message_size(uint8_t *data, size_t size) +{ + size_t r_size; + + assert(data); + + if (size < BUXTON_MESSAGE_HEADER_LENGTH) { + return 0; + } + + r_size = *(uint32_t*)(data + BUXTON_LENGTH_OFFSET); + + if (r_size < BUXTON_MESSAGE_HEADER_LENGTH) { + return 0; + } + + return r_size; +} + +void include_serialize(void) +{ + ; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/serialize.h b/src/shared/serialize.h new file mode 100644 index 0000000..3479fa8 --- /dev/null +++ b/src/shared/serialize.h @@ -0,0 +1,133 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file serialize.h Internal header + * This file is used internally by buxton to provide serialization + * functionality + */ +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include "buxton.h" +#include "buxtonarray.h" + +/** + * Magic for Buxton messages + */ +#define BUXTON_CONTROL_CODE 0x672 + +/** + * Location of size in serialized message data + */ +#define BUXTON_LENGTH_OFFSET sizeof(uint32_t) + +/** + * Minimum size of serialized BuxtonData + * 2 is the minimum number of characters in a valid SMACK label + * 0 is the mimimum number of characters in a valid value (NULL STRING) + */ +#define BXT_MINIMUM_SIZE sizeof(BuxtonDataType) \ + + (sizeof(uint32_t) * 2) \ + + 2 + +/** + * Length of valid message header + */ +#define BUXTON_MESSAGE_HEADER_LENGTH sizeof(uint32_t) \ + + sizeof(uint32_t) +/** + * Maximum length of valid control message + */ +#define BUXTON_MESSAGE_MAX_LENGTH 32768 + +/** + * Maximum length of valid control message + */ +#define BUXTON_MESSAGE_MAX_PARAMS 16 + +/** + * Serialize data internally for backend consumption + * @param source Data to be serialized + * @param label Label to be serialized + * @param target Pointer to store serialized data in + * @return a size_t value, indicating the size of serialized data + */ +size_t buxton_serialize(BuxtonData *source, BuxtonString *label, + uint8_t **target) + __attribute__((warn_unused_result)); + +/** + * Deserialize internal data for client consumption + * @param source Serialized data pointer + * @param target A pointer where the deserialize data will be stored + * @param label A pointer where the deserialize label will be stored + */ +void buxton_deserialize(uint8_t *source, BuxtonData *target, + BuxtonString *label); + +/** + * Serialize an internal buxton message for wire communication + * @param dest Pointer to store serialized message in + * @param message The type of message to be serialized + * @param msgid The message ID to be serialized + * @param list An array of BuxtonData's to be serialized + * @return a size_t, 0 indicates failure otherwise size of dest + */ +size_t buxton_serialize_message(uint8_t **dest, + BuxtonControlMessage message, + uint32_t msgid, + BuxtonArray *list) + __attribute__((warn_unused_result)); + +/** + * Deserialize the given data into an array of BuxtonData structs + * @param data The source data to be deserialized + * @param r_message An empty pointer that will be set to the message type + * @param size The size of the data being deserialized + * @param r_msgid The message ID being deserialized + * @param list A pointer that will be filled out as an array of BuxtonData structs + * @return the length of the array, or -1 if deserialization failed + */ +ssize_t buxton_deserialize_message(uint8_t *data, + BuxtonControlMessage *r_message, + size_t size, uint32_t *r_msgid, + BuxtonData **list) + __attribute__((warn_unused_result)); + +/** + * Get size of a buxton message data stream + * @param data The source data stream + * @param size The size of the data stream (from read) + * @return a size_t length of the complete message or 0 + */ +size_t buxton_get_message_size(uint8_t *data, size_t size) + __attribute__((warn_unused_result)); + +void include_serialize(void); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/shared/util.c b/src/shared/util.c new file mode 100644 index 0000000..5cad2ef --- /dev/null +++ b/src/shared/util.c @@ -0,0 +1,353 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include + +#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: + */ diff --git a/src/shared/util.h b/src/shared/util.h new file mode 100644 index 0000000..ebc53e6 --- /dev/null +++ b/src/shared/util.h @@ -0,0 +1,238 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "macro.h" +#include "buxton.h" +#include "buxtonkey.h" +#include "backend.h" + +size_t page_size(void); +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +#define buxton_string_pack(s) ((BuxtonString){(s), (uint32_t)strlen(s) + 1}) + +#define streq(a,b) (strcmp((a),(b)) == 0) +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) +#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) +#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) + +bool streq_ptr(const char *a, const char *b) _pure_; + +static inline void freep(void *p) +{ + free(*(void**) p); +} + +static inline void free_buxton_data(void *p) +{ + BuxtonData *s = (*(void**) p); + if (s && s->type == STRING) { + free(s->store.d_string.value); + } + free(s); +} +static inline void free_buxton_string(void *p) +{ + BuxtonString *s = (*(void**) p); + if (s) { + free(s->value); + } + free(s); +} + +static inline void free_buxton_key(void *p) +{ + _BuxtonKey *k = (*(void**) p); + if (k) { + if (k->group.value) { + free(k->group.value); + } + if (k->name.value) { + free(k->name.value); + } + if (k->layer.value) { + free(k->layer.value); + } + } + free(k); +} + +#define _cleanup_free_ _cleanup_(freep) +#define _cleanup_buxton_data_ _cleanup_(free_buxton_data) +#define _cleanup_buxton_string_ _cleanup_(free_buxton_string) +#define _cleanup_buxton_key_ _cleanup_(free_buxton_key) + +#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) + +#define new0(t, n) ((t*) calloc((n), sizeof(t))) + +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) + +#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) + +#define malloc0(n) (calloc((n), 1)) + +_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) { + if (_unlikely_(b == 0 || a > ((size_t) -1) / b)) { + return NULL; + } + + return malloc(a * b); +} +void* greedy_realloc(void **p, size_t *allocated, size_t need); + +/** + * Get the string representation of a BuxtonDataType + * @param type The BuxtonDataType to query + * @return A string representation of the BuxtonDataType + */ +const char* buxton_type_as_string(BuxtonDataType type) + __attribute__((warn_unused_result)); + +/** + * Retrieve the filesystem path for the given layer + * @param layer The layer in question + * @return a string containing the filesystem path + */ +char* get_layer_path(BuxtonLayer *layer) + __attribute__((warn_unused_result)); + +/** + * Perform a deep copy of one BuxtonData to another + * @param original The data being copied + * @param copy Pointer where original should be copied to + * @return A boolean indicating success or failure + */ +bool buxton_data_copy(BuxtonData *original, BuxtonData *copy); + +/** + * Perform a deep copy of one BuxtonString to another + * @param original The BuxtonString being copied + * @param copy Pointer where original should be copied to + */ +bool buxton_string_copy(BuxtonString *original, BuxtonString *copy) + __attribute__((warn_unused_result)); + +/** + * Perform a deep copy of one _BuxtonKey to another + * @param original The _BuxtonKey being copied + * @param copy Pointer where original should be copied to + */ +bool buxton_key_copy(_BuxtonKey *original, _BuxtonKey *copy) + __attribute__((warn_unused_result)); + +/** + * Perform a partial deep copy of a _BuxtonKey, omitting the 'name' member + * @param original The _BuxtonKey being copied + * @param group Pointer to the destination of the partial copy + */ +bool buxton_copy_key_group(_BuxtonKey *original, _BuxtonKey *group) + __attribute__((warn_unused_result)); + +/** + * Perform a deep free of BuxtonData + * @param data The BuxtonData being free'd + */ +void data_free(BuxtonData *data); + +/** + * Perform a deep free of BuxtonString + * @param string The BuxtonString being free'd + */ +void string_free(BuxtonString *string); + +/** + * Perform a deep free of _BuxtonKey + * @param string The _BuxtonKey being free'd + */ +void key_free(_BuxtonKey *key); + +/** + * Get the group portion of a buxton key + * @param key Pointer to _BuxtonKey + * @return A pointer to a character string containing the key's group + */ +char *get_group(_BuxtonKey *key) + __attribute__((warn_unused_result)); + +/** + * Get the name portion of a buxton key + * @param key Pointer to _BuxtonKey + * @return A pointer to a character string containing the key's name + */ +char *get_name(_BuxtonKey *key) + __attribute__((warn_unused_result)); + +/** + * Get the layer portion of a buxton key + * @param key Pointer to _BuxtonKey + * @return A pointer to a character string containing the key's layer + */ +char *get_layer(_BuxtonKey *key) + __attribute__((warn_unused_result)); + +/** + * Wrapper for nonblocking write, handles EAGAIN + * @param fd File descriptor to write to + * @param buf Buffer containing data to write + * @param len Length of buffer to write + * @return A boolean indicating the success of the operation + */ +bool _write(int fd, uint8_t *buf, size_t len) + __attribute__((warn_unused_result)); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_buxton.c b/test/check_buxton.c new file mode 100644 index 0000000..883ae7e --- /dev/null +++ b/test/check_buxton.c @@ -0,0 +1,1229 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "backend.h" +#include "buxton.h" +#include "buxtonresponse.h" +#include "check_utils.h" +#include "configurator.h" +#include "direct.h" +#include "protocol.h" +#include "serialize.h" +#include "util.h" + +#ifdef NDEBUG +#error "re-run configure with --enable-debug" +#endif + +#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK" + +START_TEST(buxton_direct_open_check) +{ + BuxtonControl c; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_direct_init_db_check) +{ + BuxtonControl c; + BuxtonString slayer_name = buxton_string_pack("base"); + BuxtonString ulayer_name = buxton_string_pack("user"); + char db[PATH_MAX]; + int r; + struct stat st; + + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + + fail_if(!buxton_direct_init_db(&c, &ulayer_name), + "Failed to run init_db for user"); + + sprintf(db, "%s/user-%d.db", buxton_db_path(), getuid()); + r = stat(db, &st); + fail_if(r != -1 && errno != ENOENT, "user db file created"); + + fail_if(!buxton_direct_init_db(&c, &slayer_name), + "Failed to run init_db"); + + sprintf(db, "%s/base.db", buxton_db_path()); + r = stat(db, &st); + fail_if(r != 0, "Failed to create db file"); + + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_direct_create_group_check) +{ + BuxtonControl c; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + _BuxtonKey group; + + group.layer = buxton_string_pack("base"); + group.group = buxton_string_pack("tgroup"); + group.name = (BuxtonString){ NULL, 0 }; + group.type = STRING; + fail_if(!buxton_direct_create_group(&c, &group, NULL), + "Create group failed"); +} +END_TEST + +START_TEST(buxton_direct_remove_group_check) +{ + BuxtonControl c; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + _BuxtonKey group; + + group.layer = buxton_string_pack("base"); + group.group = buxton_string_pack("tgroup"); + group.name = (BuxtonString){ NULL, 0 }; + group.type = STRING; + fail_if(!buxton_direct_remove_group(&c, &group, NULL), + "Failed to remove group"); +} +END_TEST + +START_TEST(buxton_direct_set_value_check) +{ + BuxtonControl c; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + _BuxtonKey group; + BuxtonString glabel; + _BuxtonKey key; + BuxtonData data; + + group.layer = buxton_string_pack("test-gdbm"); + group.group = buxton_string_pack("bxt_test_group"); + group.name = (BuxtonString){ NULL, 0 }; + group.type = STRING; + glabel = buxton_string_pack("*"); + + key.layer = group.layer; + key.group = group.group; + key.name = buxton_string_pack("bxt_test_key"); + key.type = STRING; + + c.client.uid = getuid(); + fail_if(buxton_direct_create_group(&c, &group, NULL) == false, + "Creating group failed."); + fail_if(buxton_direct_set_label(&c, &group, &glabel) == false, + "Setting group label failed."); + data.type = STRING; + data.store.d_string = buxton_string_pack("bxt_test_value"); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Setting value in buxton directly failed."); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_direct_get_value_for_layer_check) +{ + BuxtonControl c; + BuxtonData result; + BuxtonString dlabel; + _BuxtonKey key; + + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("bxt_test_group"); + key.name = buxton_string_pack("bxt_test_key"); + key.type = STRING; + + c.client.uid = getuid(); + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Retrieving value from buxton gdbm backend failed."); + fail_if(result.type != STRING, + "Buxton gdbm backend returned incorrect result type."); + //FIXME get label test figured out + fail_if(strcmp(result.store.d_string.value, "bxt_test_value") != 0, + "Buxton gdbm returned a different value to that set."); + if (result.store.d_string.value) + free(result.store.d_string.value); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_direct_get_value_check) +{ + BuxtonControl c; + BuxtonData data, result; + BuxtonString dlabel; + _BuxtonKey key; + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("bxt_test_group"); + key.name = buxton_string_pack("bxt_test_key"); + key.type = STRING; + + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + + c.client.uid = getuid(); + data.type = STRING; + data.store.d_string = buxton_string_pack("bxt_test_value2"); + fail_if(data.store.d_string.value == NULL, + "Failed to allocate test string."); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Failed to set second value."); + fail_if(buxton_direct_get_value(&c, &key, &result, &dlabel, NULL) == -1, + "Retrieving value from buxton gdbm backend failed."); + fail_if(result.type != STRING, + "Buxton gdbm backend returned incorrect result type."); + //FIXME figure out label check + fail_if(strcmp(result.store.d_string.value, "bxt_test_value2") != 0, + "Buxton gdbm returned a different value to that set."); + if (result.store.d_string.value) + free(result.store.d_string.value); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_memory_backend_check) +{ + BuxtonControl c; + BuxtonData data, result; + BuxtonString dlabel, glabel; + _BuxtonKey group; + _BuxtonKey key; + + group.layer = buxton_string_pack("temp"); + group.group = buxton_string_pack("bxt_mem_test_group"); + group.name = (BuxtonString){ NULL, 0 }; + group.type = STRING; + glabel = buxton_string_pack("*"); + + key.layer = group.layer; + key.group = group.group; + key.name = buxton_string_pack("bxt_mem_test_key"); + key.type = STRING; + + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + + c.client.uid = getuid(); + fail_if(buxton_direct_create_group(&c, &group, NULL) == false, + "Creating group failed."); + fail_if(buxton_direct_set_label(&c, &group, &glabel) == false, + "Setting group label failed."); + data.type = STRING; + data.store.d_string = buxton_string_pack("bxt_test_value"); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Setting value in buxton memory backend directly failed."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Retrieving value from buxton memory backend directly failed."); + // FIXME: BUXTON_GROUP_VALUE is the dummy group data value, but the memory + // backend doesn't understand groups, so this is the current workaround. + fail_if(!streq(result.store.d_string.value, "bxt_test_value"), + "Buxton memory returned a different value to that set."); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_key_check) +{ + char *group = "group"; + char *name = "name"; + char *layer = "layer"; + BuxtonKey key; + char *g; + char *n; + char *l; + BuxtonDataType type = STRING; + + key = buxton_key_create(group, name, layer, type); + fail_if(!key, "Failed to create buxton key"); + g = buxton_key_get_group(key); + n = buxton_key_get_name(key); + l = buxton_key_get_layer(key); + fail_if(!g, "Failed to get group from key"); + fail_if(!n, "Failed to get name from key"); + fail_if(!l, "Failed to get layer from key"); + fail_if(buxton_key_get_type(key) != type, + "Failed to get correct type from key"); + fail_if(!streq(g, group), "Got different group back from key"); + fail_if(!streq(n, name), "Got different name back from key"); + fail_if(!streq(l, layer), "Got different layer back from key"); + free(g); + free(n); + free(l); + buxton_key_free(key); + + fail_if(buxton_key_create(NULL, name, layer, type), "Got key back with invalid group"); + fail_if(buxton_key_create(group, name, layer, BUXTON_TYPE_MIN), + "Got key back with invalid type 1"); + fail_if(buxton_key_create(group, name, layer, BUXTON_TYPE_MAX), + "Got key back with invalid type 2"); + + key = buxton_key_create(group, NULL, layer, type); + fail_if(!key, "Failed to create buxton key with empty name"); + fail_if(buxton_key_get_name(key), "Got name back with no name key"); + buxton_key_free(key); + + key = buxton_key_create(group, name, NULL, type); + fail_if(!key, "Failed to create buxton key with empty layer"); + fail_if(buxton_key_get_layer(key), "Got layer back with no layer key"); + buxton_key_free(key); +} +END_TEST + +START_TEST(buxton_set_label_check) +{ + BuxtonControl c; + BuxtonString label = buxton_string_pack("*"); + _BuxtonKey key; + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("bxt_test"); + key.name.value = NULL; + key.type = STRING; + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + c.client.uid = 0; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + fail_if(buxton_direct_create_group(&c, &key, NULL) == false, + "Creating group failed."); + fail_if(buxton_direct_set_label(&c, &key, &label) == false, + "Failed to set label as root user."); + + c.client.uid = 1000; + + if (skip_check) + fail_if(!buxton_direct_set_label(&c, &key, &label), + "Unable to set label with root check disabled"); + else + fail_if(buxton_direct_set_label(&c, &key, &label), + "Able to set label as non-root user."); + + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_group_label_check) +{ + BuxtonControl c; + BuxtonData result; + BuxtonString dlabel; + BuxtonString label = buxton_string_pack("*"); + _BuxtonKey key; + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("test-group"); + key.name.value = NULL; + key.type = STRING; + + + c.client.uid = 0; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + fail_if(buxton_direct_create_group(&c, &key, NULL) == false, + "Creating group failed."); + fail_if(buxton_direct_set_label(&c, &key, &label) == false, + "Failed to set group label."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Retrieving group label failed."); + fail_if(!streq("*", dlabel.value), + "Retrieved group label is incorrect."); + + free(dlabel.value); + buxton_direct_close(&c); +} +END_TEST + +START_TEST(buxton_name_label_check) +{ + BuxtonControl c; + BuxtonData data, result; + BuxtonString label, dlabel; + _BuxtonKey key; + + /* create the group first, and validate the label */ + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("group-foo"); + key.name.value = NULL; + key.type = STRING; + label = buxton_string_pack("*"); + + c.client.uid = 0; + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + fail_if(buxton_direct_create_group(&c, &key, NULL) == false, + "Creating group failed."); + fail_if(buxton_direct_set_label(&c, &key, &label) == false, + "Failed to set group label."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Retrieving group label failed."); + fail_if(!streq("*", dlabel.value), + "Retrieved group label is incorrect."); + free(dlabel.value); + free(result.store.d_string.value); + + /* then create the name (key), and validate the label */ + key.name = buxton_string_pack("name-foo"); + data.type = STRING; + data.store.d_string = buxton_string_pack("value1-foo"); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Failed to set key name-foo."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Failed to get value for name-foo 1"); + fail_if(!streq("value1-foo", result.store.d_string.value), + "Retrieved key value is incorrect 1"); + fail_if(!streq(dlabel.value, "_"), "Failed to set default label"); + free(dlabel.value); + free(result.store.d_string.value); + fail_if(buxton_direct_set_label(&c, &key, &label) == false, + "Failed to set name label."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Failed to get value for name-foo 2"); + fail_if(!streq("value1-foo", result.store.d_string.value), + "Retrieved key value is incorrect 2"); + fail_if(!streq("*", dlabel.value), + "Retrieved key label is incorrect."); + free(dlabel.value); + free(result.store.d_string.value); + + /* modify the same key, with a new value, and validate the label */ + data.store.d_string = buxton_string_pack("value2-foo"); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Failed to modify key name-foo."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Failed to get new value for name-foo."); + fail_if(!streq("value2-foo", result.store.d_string.value), + "New key value is incorrect."); + fail_if(!streq("*", dlabel.value), + "Key label has been modified."); + + /* modify the key label directly once it has been created */ + fail_if(buxton_direct_set_label(&c, &key, &label) == false, + "Failed to modify label on key."); + + free(dlabel.value); + free(result.store.d_string.value); + + buxton_direct_close(&c); +} +END_TEST + +static int run_callback_test_value = 0; +static void run_callback_cb_test(BuxtonResponse response, void *data) +{ + _BuxtonResponse *r = (_BuxtonResponse *)response; + BuxtonData *d; + bool *b; + + fail_if(r->type != BUXTON_CONTROL_SET, "Unexpected response type"); + + fail_if(!streq(r->key->group.value, "group"), + "Failed to set key's group"); + + switch (run_callback_test_value) { + case 0: + /* first pass through */ + run_callback_test_value = 1; + break; + case 1: + /* second pass through */ + fail_if(r->data->len != 1, "Failed setup array size"); + d = buxton_array_get(r->data, 0); + fail_if(!d, "Failed to set array element"); + fail_if(d->type != INT32, "Failed to setup array element value"); + run_callback_test_value = 2; + break; + case 2: + /* third pass through */ + b = (bool *)data; + *b = true; + break; + default: + fail("Unexpected test value"); + break; + } +} +START_TEST(run_callback_check) +{ + bool data = false; + BuxtonData list[] = { + {INT32, {.d_int32 = 1}} + }; + _BuxtonKey key; + key.group = buxton_string_pack("group"); + + run_callback(NULL, (void *)&data, 1, list, BUXTON_CONTROL_SET, &key); + run_callback(run_callback_cb_test, NULL, 0, NULL, BUXTON_CONTROL_SET, + &key); + fail_if(run_callback_test_value != 1, + "Failed to update callback test value 1"); + run_callback(run_callback_cb_test, NULL, 1, list, BUXTON_CONTROL_SET, + &key); + fail_if(run_callback_test_value != 2, + "Failed to update callback test value 2"); + run_callback(run_callback_cb_test, (void *)&data, 0, NULL, + BUXTON_CONTROL_SET, &key); + fail_if(!data, "Failed to update callback test value 3"); +} +END_TEST + +START_TEST(send_message_check) +{ + _BuxtonClient client; + BuxtonArray *out_list = NULL; + BuxtonData *list = NULL; + int server; + uint8_t *dest = NULL; + uint8_t *source = NULL; + size_t size; + BuxtonData data; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to setup callbacks"); + + out_list = buxton_array_new(); + data.type = INT32; + data.store.d_int32 = 0; + fail_if(!buxton_array_add(out_list, &data), + "Failed to add get response to array"); + size = buxton_serialize_message(&source, BUXTON_CONTROL_STATUS, + 0, out_list); + fail_if(size == 0, "Failed to serialize message"); + fail_if(!send_message(&client, source, size, NULL, NULL, 0, + BUXTON_CONTROL_STATUS, NULL), + "Failed to write message 1"); + + cleanup_callbacks(); + buxton_array_free(&out_list, NULL); + free(dest); + free(list); + close(server); + close(client.fd); +} +END_TEST + +static void handle_response_cb_test(_BuxtonResponse *response, void *data) +{ + bool *val = (bool *)data; + fail_if(!(*val), "Got unexpected response data"); + *val = false; +} +START_TEST(handle_callback_response_check) +{ + _BuxtonClient client; + BuxtonArray *out_list = NULL; + uint8_t *dest = NULL; + int server; + size_t size; + bool test_data; + BuxtonData data; + uint32_t msgid; + BuxtonData good[] = { + {INT32, {.d_int32 = 0}} + }; + BuxtonData good_unnotify[] = { + {INT32, {.d_int32 = 0 }}, + {STRING, {.d_string = {0}}}, + {UINT32, {.d_uint32 = 4 }} + }; + BuxtonData bad1[] = { + {INT64, {.d_int64 = 0}} + }; + BuxtonData bad2[] = { + {INT32, {.d_int32 = 1}} + }; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + /* done just to create a callback to be used */ + fail_if(!setup_callbacks(), + "Failed to initialeze response callbacks"); + out_list = buxton_array_new(); + data.type = INT32; + data.store.d_int32 = 0; + msgid = 1; + fail_if(!buxton_array_add(out_list, &data), + "Failed to add data to array"); + size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS, + msgid, out_list); + buxton_array_free(&out_list, NULL); + fail_if(size == 0, "Failed to serialize message"); + + test_data = true; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_SET, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1); + fail_if(test_data, "Failed to set cb data non notify type"); + + test_data = true; + msgid = 2; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1); + fail_if(test_data, "Failed to set notify bad1 data"); + + test_data = true; + msgid = 3; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad2, 1); + fail_if(test_data, "Failed to set notify bad2 data"); + + test_data = true; + msgid = 4; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good, 1); + fail_if(!test_data, "Set notify good data"); + + /* ensure we run callback on duplicate msgid */ + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_NOTIFY, NULL), + "Failed to send message %d-2", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good, 1); + fail_if(test_data, "Failed to set notify duplicate msgid"); + + test_data = true; + handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1); + fail_if(test_data, "Failed to set changed data"); + + /* ensure we don't remove callback on changed */ + test_data = true; + handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1); + fail_if(test_data, "Failed to set changed data"); + + test_data = true; + msgid = 6; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad1, 1); + fail_if(test_data, "Failed to set unnotify bad1 data"); + + test_data = true; + msgid = 7; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, bad2, 1); + fail_if(test_data, "Failed to set unnotify bad2 data"); + + test_data = true; + msgid = 8; + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, msgid, BUXTON_CONTROL_UNNOTIFY, NULL), + "Failed to send message %d", msgid); + handle_callback_response(BUXTON_CONTROL_STATUS, msgid, good_unnotify, 1); + fail_if(!test_data, "Set unnotify good data"); + + test_data = true; + msgid = 4; + handle_callback_response(BUXTON_CONTROL_CHANGED, msgid, good, 1); + fail_if(!test_data, "Didn't remove changed callback"); + + cleanup_callbacks(); + free(dest); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_handle_response_check) +{ + _BuxtonClient client; + BuxtonArray *out_list = NULL; + int server; + uint8_t *dest = NULL; + size_t size; + BuxtonData data; + bool test_data = true; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + /* done just to create a callback to be used */ + fail_if(!setup_callbacks(), + "Failed to initialeze get response callbacks"); + out_list = buxton_array_new(); + data.type = INT32; + data.store.d_int32 = 0; + fail_if(!buxton_array_add(out_list, &data), + "Failed to add get response to array"); + size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS, + 0, out_list); + buxton_array_free(&out_list, NULL); + fail_if(size == 0, "Failed to serialize message"); + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, 0, BUXTON_CONTROL_STATUS, NULL), + "Failed to send message"); + + /* server */ + fail_if(!_write(server, dest, size), + "Failed to send get response"); + + /* client */ + fail_if(buxton_wire_handle_response(&client) != 1, + "Failed to handle response correctly"); + fail_if(test_data, "Failed to update data"); + + cleanup_callbacks(); + free(dest); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_get_response_check) +{ + _BuxtonClient client; + BuxtonArray *out_list = NULL; + int server; + uint8_t *dest = NULL; + size_t size; + BuxtonData data; + bool test_data = true; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + /* done just to create a callback to be used */ + fail_if(!setup_callbacks(), + "Failed to initialeze callbacks"); + out_list = buxton_array_new(); + data.type = INT32; + data.store.d_int32 = 0; + fail_if(!buxton_array_add(out_list, &data), + "Failed to add data to array"); + size = buxton_serialize_message(&dest, BUXTON_CONTROL_STATUS, + 0, out_list); + buxton_array_free(&out_list, NULL); + fail_if(size == 0, "Failed to serialize message"); + fail_if(!send_message(&client, dest, size, handle_response_cb_test, + &test_data, 0, BUXTON_CONTROL_STATUS, NULL), + "Failed to send message"); + + /* server */ + fail_if(!_write(server, dest, size), + "Failed to send get response"); + + /* client */ + fail_if(!buxton_wire_get_response(&client), + "Failed to handle response correctly"); + fail_if(test_data, "Failed to update data"); + + cleanup_callbacks(); + free(dest); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_set_value_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialeze callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + key.type = STRING; + + fail_if(buxton_wire_set_value(&client, &key, "value", NULL, + NULL) != true, + "Failed to properly set value"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 4, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_SET, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(list[2].type != STRING, "Failed to set correct name type"); + fail_if(list[3].type != STRING, "Failed to set correct value type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + fail_if(!streq(list[2].store.d_string.value, "name"), + "Failed to set correct name"); + fail_if(!streq(list[3].store.d_string.value, "value"), + "Failed to set correct value"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list[3].store.d_string.value); + free(list); + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_set_label_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonString value; + BuxtonControlMessage msg; + uint32_t msgid; + + client.uid = 0; + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialize callbacks"); + + /* first, set a label on a group */ + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name.value = NULL; + value = buxton_string_pack("*"); + fail_if(buxton_wire_set_label(&client, &key, &value, NULL, + NULL) != true, + "Failed to properly set label"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 3, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_SET_LABEL, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(list[2].type != STRING, "Failed to set correct label type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + fail_if(!streq(list[2].store.d_string.value, "*"), + "Failed to set correct label"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list); + + /* ... then, set a label on a key */ + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + value = buxton_string_pack("*"); + fail_if(buxton_wire_set_label(&client, &key, &value, NULL, + NULL) != true, + "Failed to properly set label"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 4, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_SET_LABEL, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(list[2].type != STRING, "Failed to set correct name type"); + fail_if(list[3].type != STRING, "Failed to set correct label type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + fail_if(!streq(list[2].store.d_string.value, "name"), + "Failed to set correct name"); + fail_if(!streq(list[3].store.d_string.value, "*"), + "Failed to set correct label"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list[3].store.d_string.value); + free(list); + + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_get_value_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialeze callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + key.type = STRING; + fail_if(buxton_wire_get_value(&client, &key, NULL, + NULL) != true, + "Failed to properly get value 1"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed 1"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 4, "Failed to get valid message from buffer 1"); + fail_if(msg != BUXTON_CONTROL_GET, + "Failed to get correct control type 1"); + fail_if(list[0].type != STRING, "Failed to set correct layer type 1"); + fail_if(list[1].type != STRING, "Failed to set correct group type 1"); + fail_if(list[2].type != STRING, "Failed to set correct name type 1"); + fail_if(list[3].type != UINT32, "Failed to set correct type type 1"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer 1"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group 1"); + fail_if(!streq(list[2].store.d_string.value, "name"), + "Failed to set correct name 1"); + fail_if(list[3].store.d_uint32 != STRING, + "Failed to set correct type 1"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list); + + key.layer.value = NULL; + fail_if(buxton_wire_get_value(&client, &key, NULL, + NULL) != true, + "Failed to properly get value 2"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed 2"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 3, "Failed to get valid message from buffer 2"); + fail_if(msg != BUXTON_CONTROL_GET, + "Failed to get correct control type 2"); + fail_if(list[0].type != STRING, "Failed to set correct group type 2"); + fail_if(list[1].type != STRING, "Failed to set correct name type 2"); + fail_if(list[2].type != UINT32, "Failed to set correct type type 2"); + fail_if(!streq(list[0].store.d_string.value, "group"), + "Failed to set correct group 2"); + fail_if(!streq(list[1].store.d_string.value, "name"), + "Failed to set correct name 2"); + fail_if(list[2].store.d_uint32 != STRING, + "Failed to set correct type 2"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list); + + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_unset_value_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialeze callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + key.type = STRING; + fail_if(!buxton_wire_unset_value(&client, &key, NULL, + NULL), + "Failed to properly unset value"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 4, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_UNSET, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(list[2].type != STRING, "Failed to set correct name type"); + fail_if(list[3].type != UINT32, "Failed to set correct type type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + fail_if(!streq(list[2].store.d_string.value, "name"), + "Failed to set correct group"); + fail_if(list[3].store.d_uint32 != STRING, + "Failed to set correct type"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list); + + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_create_group_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + client.uid = 0; + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialize callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name.value = NULL; + fail_if(buxton_wire_create_group(&client, &key, NULL, + NULL) != true, + "Failed to send message"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 2, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_CREATE_GROUP, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list); + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +START_TEST(buxton_wire_remove_group_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + client.uid = 0; + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialize callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name.value = NULL; + fail_if(buxton_wire_remove_group(&client, &key, NULL, + NULL) != true, + "Failed to send message"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 2, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_REMOVE_GROUP, + "Failed to get correct control type"); + fail_if(list[0].type != STRING, "Failed to set correct layer type"); + fail_if(list[1].type != STRING, "Failed to set correct group type"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list); + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + +static Suite * +buxton_suite(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("buxton"); + + tc = tcase_create("buxton_client_lib_functions"); + tcase_add_test(tc, buxton_direct_init_db_check); + tcase_add_test(tc, buxton_direct_open_check); + tcase_add_test(tc, buxton_direct_create_group_check); + tcase_add_test(tc, buxton_direct_remove_group_check); + tcase_add_test(tc, buxton_direct_set_value_check); + tcase_add_test(tc, buxton_direct_get_value_for_layer_check); + tcase_add_test(tc, buxton_direct_get_value_check); + tcase_add_test(tc, buxton_memory_backend_check); + tcase_add_test(tc, buxton_key_check); + tcase_add_test(tc, buxton_set_label_check); + tcase_add_test(tc, buxton_group_label_check); + tcase_add_test(tc, buxton_name_label_check); + suite_add_tcase(s, tc); + + tc = tcase_create("buxton_protocol_functions"); + tcase_add_test(tc, run_callback_check); + tcase_add_test(tc, handle_callback_response_check); + tcase_add_test(tc, send_message_check); + tcase_add_test(tc, buxton_wire_handle_response_check); + tcase_add_test(tc, buxton_wire_get_response_check); + tcase_add_test(tc, buxton_wire_set_value_check); + tcase_add_test(tc, buxton_wire_set_label_check); + tcase_add_test(tc, buxton_wire_get_value_check); + tcase_add_test(tc, buxton_wire_unset_value_check); + tcase_add_test(tc, buxton_wire_create_group_check); + tcase_add_test(tc, buxton_wire_remove_group_check); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf"); + putenv("BUXTON_ROOT_CHECK=0"); + s = buxton_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_configurator.c b/test/check_configurator.c new file mode 100644 index 0000000..d18881b --- /dev/null +++ b/test/check_configurator.c @@ -0,0 +1,346 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "configurator.h" + +#ifdef NDEBUG + #error "re-run configure with --enable-debug" +#endif + +static void fail_strne(const char *value, char *correct_value, bool casecmp) +{ + char buf[PATH_MAX]; + int ret; + + snprintf(buf, sizeof(buf), "%s was not %s", value, correct_value); + if (casecmp) + ret = strcasecmp(value, correct_value); + else + ret = strcmp(value, correct_value); + fail_unless(ret == 0, buf); +} + +static void fail_ne(int a, int b) +{ + fail_unless(a == b, "%d is not %d", a, b); +} + +static void default_test(const char *value, char *correct_value, char *symbolname) +{ + char buf[PATH_MAX]; + + snprintf(buf, sizeof(buf), "%s returned null!", symbolname); + fail_if(value == NULL, buf); + + snprintf(buf, sizeof(buf), "%s was not %s", value, correct_value); + fail_strne(value, correct_value, true); +} + +START_TEST(configurator_default_module_dir) +{ + default_test(buxton_module_dir(), (char*)_MODULE_DIRECTORY, "buxton_module_dir()"); +} +END_TEST + +START_TEST(configurator_default_conf_file) +{ + default_test(buxton_conf_file(), (char*)_DEFAULT_CONFIGURATION_FILE, "buxton_conf_file()"); +} +END_TEST + +START_TEST(configurator_default_db_path) +{ + default_test(buxton_db_path(), (char*)_DB_PATH, "buxton_db_path()"); +} +END_TEST + +START_TEST(configurator_default_smack_load_file) +{ + default_test(buxton_smack_load_file(), (char*)_SMACK_LOAD_FILE, "buxton_smack_load_file()"); +} +END_TEST + +START_TEST(configurator_default_buxton_socket) +{ + default_test(buxton_socket(), (char*)_BUXTON_SOCKET, "buxton_socket()"); +} +END_TEST + + +START_TEST(configurator_env_conf_file) +{ + putenv("BUXTON_CONF_FILE=/nonexistant/buxton.conf"); + default_test(buxton_conf_file(), "/nonexistant/buxton.conf", "buxton_conf_file()"); +} +END_TEST + +START_TEST(configurator_env_module_dir) +{ + putenv("BUXTON_MODULE_DIR=/nonexistant/buxton/plugins"); + default_test(buxton_module_dir(), "/nonexistant/buxton/plugins", "buxton_module_dir()"); +} +END_TEST + +START_TEST(configurator_env_db_path) +{ + putenv("BUXTON_DB_PATH=/nonexistant/buxton.db"); + default_test(buxton_db_path(), "/nonexistant/buxton.db", "buxton_db_path()"); +} +END_TEST + +START_TEST(configurator_env_smack_load_file) +{ + putenv("BUXTON_SMACK_LOAD_FILE=/nonexistant/smack_load_file"); + default_test(buxton_smack_load_file(), "/nonexistant/smack_load_file", "buxton_smack_load_file()"); +} +END_TEST + +START_TEST(configurator_env_buxton_socket) +{ + putenv("BUXTON_BUXTON_SOCKET=/nonexistant/buxton_socket"); + default_test(buxton_socket(), "/nonexistant/buxton_socket", "buxton_socket()"); +} +END_TEST + + +START_TEST(configurator_cmd_conf_file) +{ + char *correct = "herpyderpy"; + + buxton_add_cmd_line(CONFIG_CONF_FILE, correct); + putenv("BUXTON_CONF_FILE=/nonexistant/buxton.conf"); + default_test(buxton_conf_file(), correct, "buxton_conf_file()"); +} +END_TEST + +START_TEST(configurator_cmd_module_dir) +{ + char *correct = "herpyderpy"; + + buxton_add_cmd_line(CONFIG_MODULE_DIR, correct); + putenv("BUXTON_MODULE_DIR=/nonexistant/buxton/plugins"); + default_test(buxton_module_dir(), correct, "buxton_module_dir()"); +} +END_TEST + +START_TEST(configurator_cmd_db_path) +{ + char *correct = "herpyderpy"; + + buxton_add_cmd_line(CONFIG_DB_PATH, correct); + putenv("BUXTON_DB_PATH=/nonexistant/buxton.db"); + default_test(buxton_db_path(), correct, "buxton_db_path()"); +} +END_TEST + +START_TEST(configurator_cmd_smack_load_file) +{ + char *correct = "herpyderpy"; + + buxton_add_cmd_line(CONFIG_SMACK_LOAD_FILE, correct); + putenv("BUXTON_SMACK_LOAD_FILE=/nonexistant/smack_load_file"); + default_test(buxton_smack_load_file(), correct, "buxton_smack_load_file()"); +} +END_TEST + +START_TEST(configurator_cmd_buxton_socket) +{ + char *correct = "herpyderpy"; + + buxton_add_cmd_line(CONFIG_BUXTON_SOCKET, correct); + putenv("BUXTON_BUXTON_SOCKET=/nonexistant/buxton_socket"); + default_test(buxton_socket(), correct, "buxton_socket()"); +} +END_TEST + + +START_TEST(configurator_conf_db_path) +{ + char *correct = "/you/are/so/suck"; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf"); + default_test(buxton_db_path(), correct, "buxton_db_path()"); +} +END_TEST + +START_TEST(configurator_conf_smack_load_file) +{ + char *correct = "/smack/smack/smack"; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf"); + default_test(buxton_smack_load_file(), correct, "smack_load_file()"); +} +END_TEST + +START_TEST(configurator_conf_buxton_socket) +{ + char *correct = "/hurp/durp/durp"; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf"); + default_test(buxton_socket(), correct, "buxton_socket()"); +} +END_TEST + +START_TEST(configurator_conf_module_dir) +{ + char *correct = "/shut/your/mouth"; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf"); + default_test(buxton_module_dir(), correct, "buxton_module_dir()"); +} +END_TEST + +START_TEST(configurator_get_layers) +{ + ConfigLayer *layers = NULL; + int numlayers; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_SRCDIR "/test/test-configurator.conf"); + numlayers = buxton_key_get_layers(&layers); + fail_if(layers == NULL, "buxton_key_get_layers returned NULL"); + fail_if(numlayers != 7, "num layers is %d instead of %d", numlayers, 7); + + fail_strne(layers[0].name, "base", false); + fail_strne(layers[0].type, "System", false); + fail_strne(layers[0].backend, "gdbm", false); + fail_strne(layers[0].description, "Operating System configuration layer", false); + fail_ne(layers[0].priority, 0); + + fail_strne(layers[1].name, "isp", false); + fail_strne(layers[1].type, "System", false); + fail_strne(layers[1].backend, "gdbm", false); + fail_strne(layers[1].description, "ISP specific settings", false); + fail_ne(layers[1].priority, 1); + + /* ... */ + + fail_strne(layers[6].name, "test-gdbm-user", false); + fail_strne(layers[6].type, "User", false); + fail_strne(layers[6].backend, "gdbm", false); + fail_strne(layers[6].description, "GDBM test db for user", false); + fail_ne(layers[6].priority, 6000); + + + +} +END_TEST + +START_TEST(ini_parse_check) +{ + char ini_good[] = "test/test-pass.ini"; + char ini_bad[] = "test/test-fail.ini"; + dictionary *ini = NULL; + + ini = iniparser_load(ini_good); + fail_if(ini == NULL, + "Failed to parse ini file"); + + iniparser_dump(ini, stdout); + iniparser_freedict(ini); + + ini = iniparser_load(ini_bad); + fail_if(ini != NULL, + "Failed to catch bad ini file"); + + iniparser_dump(ini, stdout); + iniparser_freedict(ini); +} +END_TEST + +static Suite * +configurator_suite(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("configurator"); + + tc = tcase_create("compilation defaults"); + tcase_add_test(tc, configurator_default_conf_file); + tcase_add_test(tc, configurator_default_module_dir); + tcase_add_test(tc, configurator_default_db_path); + tcase_add_test(tc, configurator_default_smack_load_file); + tcase_add_test(tc, configurator_default_buxton_socket); + suite_add_tcase(s, tc); + + tc = tcase_create("env clobbers defaults"); + tcase_add_test(tc, configurator_env_conf_file); + tcase_add_test(tc, configurator_env_module_dir); + tcase_add_test(tc, configurator_env_db_path); + tcase_add_test(tc, configurator_env_smack_load_file); + tcase_add_test(tc, configurator_env_buxton_socket); + suite_add_tcase(s, tc); + + tc = tcase_create("command line clobbers all"); + tcase_add_test(tc, configurator_cmd_conf_file); + tcase_add_test(tc, configurator_cmd_module_dir); + tcase_add_test(tc, configurator_cmd_db_path); + tcase_add_test(tc, configurator_cmd_smack_load_file); + tcase_add_test(tc, configurator_cmd_buxton_socket); + suite_add_tcase(s, tc); + + tc = tcase_create("config file works"); + tcase_add_test(tc, configurator_conf_module_dir); + tcase_add_test(tc, configurator_conf_db_path); + tcase_add_test(tc, configurator_conf_smack_load_file); + tcase_add_test(tc, configurator_conf_buxton_socket); + suite_add_tcase(s, tc); + + tc = tcase_create("config file works"); + tcase_add_test(tc, configurator_get_layers); + suite_add_tcase(s, tc); + + tc = tcase_create("ini_functions"); + tcase_add_test(tc, ini_parse_check); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = configurator_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_daemon.c b/test/check_daemon.c new file mode 100644 index 0000000..eaf253e --- /dev/null +++ b/test/check_daemon.c @@ -0,0 +1,2759 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "buxtonresponse.h" +#include "configurator.h" +#include "check_utils.h" +#include "daemon.h" +#include "direct.h" +#include "hashmap.h" +#include "log.h" +#include "smack.h" +#include "util.h" + +#ifdef NDEBUG +#error "re-run configure with --enable-debug" +#endif + +#define BUXTON_ROOT_CHECK_ENV "BUXTON_ROOT_CHECK" + +static pid_t daemon_pid; +int fuzz_time; + +typedef struct _fuzz_context_t { + uint8_t buf[4096]; + size_t size; + int iteration; +} FuzzContext; + +static bool use_smack(void) +{ + bool __attribute__((unused))dummy; + + dummy = buxton_cache_smack_rules(); + + return buxton_smack_enabled(); +} + +static char* dump_fuzz(FuzzContext *fuzz) +{ + char* buf; + size_t buff_size; + FILE* s; + int l = 0; + int c = 0; + + s = open_memstream(&buf, &buff_size); + + fprintf(s, "\n\n******************************************\n"); + fprintf(s, "current time %ld\n", time(NULL)); + fprintf(s, "iteration: %d\tsize: %llu\n", fuzz->iteration, (unsigned long long)fuzz->size); + for (int i = 0; i < fuzz->size; i++) { + fprintf(s, "%02X ", fuzz->buf[i]); + c+= 3; + if (c > 80) { + fprintf(s, "\n"); + c = 0; + l++; + } + } + fclose(s); + + return buf; +} + +static void check_did_not_crash(pid_t pid, FuzzContext *fuzz) +{ + pid_t rpid; + int status; + + rpid = waitpid(pid, &status, WNOHANG); + fail_if(rpid == -1, "couldn't wait for pid %m"); + if (rpid == 0) { + /* child is still running */ + return; + } + fail_if(WIFEXITED(status), "daemon exited with status %d%s", + WEXITSTATUS(status), dump_fuzz(fuzz)); + fail_if(WIFSIGNALED(status), "daemon was killed with signal %d%s", + WTERMSIG(status), dump_fuzz(fuzz)); +} + +static void exec_daemon(void) +{ + char path[PATH_MAX]; + + //FIXME: path is wrong for makedistcheck + snprintf(path, PATH_MAX, "%s/check_buxtond", get_current_dir_name()); + + if (execl(path, "check_buxtond", (const char*)NULL) < 0) { + fail("couldn't exec: %m"); + } + fail("should never reach here"); +} + +static void setup(void) +{ + daemon_pid = 0; + sigset_t sigset; + pid_t pid; + + unlink(buxton_socket()); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + pid = fork(); + fail_if(pid < 0, "couldn't fork"); + if (pid) { + /* parent*/ + daemon_pid = pid; + usleep(128*1000); + } else { + /* child */ + exec_daemon(); + } +} + +static void teardown(void) +{ + if (daemon_pid) { + int status; + pid_t pid; + + pid = waitpid(daemon_pid, &status, WNOHANG); + fail_if(pid == -1, "waitpid error"); + if (pid) { + fail("daemon crashed!"); + } else { + /* if the daemon is still running, kill it */ + kill(SIGTERM, daemon_pid); + usleep(64*1000); + kill(SIGKILL, daemon_pid); + } + } +} + +START_TEST(buxton_open_check) +{ + BuxtonClient c = NULL; + fail_if(buxton_open(&c) == -1, + "Connection failed to open with daemon."); +} +END_TEST + +static void client_create_group_test(BuxtonResponse response, void *data) +{ + char *k = (char *)data; + BuxtonKey key; + char *group; + uid_t uid = getuid(); + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + fail_if(buxton_response_type(response) != BUXTON_CONTROL_CREATE_GROUP, + "Failed to get create group response type"); + + if (uid == 0) { + fail_if(buxton_response_status(response) != 0, + "Create group failed"); + key = buxton_response_key(response); + fail_if(!key, "Failed to get create group key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group from key"); + fail_if(!streq(group, k), + "Incorrect set key returned"); + free(group); + buxton_key_free(key); + } else { + fail_if(buxton_response_status(response) == 0 && !skip_check, + "Create group succeeded, but the client is not root"); + } +} +START_TEST(buxton_create_group_check) +{ + BuxtonClient c; + BuxtonKey key = buxton_key_create("tgroup", NULL, "base", STRING); + fail_if(!key, "Failed to create key"); + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_create_group(c, key, client_create_group_test, + "tgroup", true), + "Creating group in buxton failed."); + buxton_key_free(key); +} +END_TEST + +static void client_remove_group_test(BuxtonResponse response, void *data) +{ + char *k = (char *)data; + BuxtonKey key; + char *group; + uid_t uid = getuid(); + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + fail_if(buxton_response_type(response) != BUXTON_CONTROL_REMOVE_GROUP, + "Failed to get remove group response type"); + + if (uid == 0) { + fail_if(buxton_response_status(response) != 0, + "Remove group failed"); + key = buxton_response_key(response); + fail_if(!key, "Failed to get create group key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group from key"); + fail_if(!streq(group, k), + "Incorrect set key returned"); + free(group); + buxton_key_free(key); + } else { + fail_if(buxton_response_status(response) == 0 && !skip_check, + "Create group succeeded, but the client is not root"); + } +} +START_TEST(buxton_remove_group_check) +{ + BuxtonClient c; + BuxtonKey key = buxton_key_create("tgroup", NULL, "base", STRING); + fail_if(!key, "Failed to create key"); + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_remove_group(c, key, client_remove_group_test, + "tgroup", true), + "Removing group in buxton failed."); + buxton_key_free(key); +} +END_TEST + +static void client_set_value_test(BuxtonResponse response, void *data) +{ + char *k = (char *)data; + BuxtonKey key; + char *group; + + fail_if(buxton_response_type(response) != BUXTON_CONTROL_SET, + "Failed to get set response type"); + fail_if(buxton_response_status(response) != 0, + "Set value failed"); + key = buxton_response_key(response); + fail_if(!key, "Failed to get set key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group from key"); + fail_if(!streq(group, k), + "Incorrect set group returned"); + free(group); + buxton_key_free(key); +} +START_TEST(buxton_set_value_check) +{ + BuxtonClient c; + BuxtonKey group = buxton_key_create("group", NULL, "test-gdbm-user", STRING); + fail_if(!group, "Failed to create key for group"); + BuxtonKey key = buxton_key_create("group", "name", "test-gdbm-user", STRING); + fail_if(!key, "Failed to create key"); + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_create_group(c, group, NULL, NULL, true), + "Creating group in buxton failed."); + fail_if(buxton_set_label(c, group, "*", NULL, NULL, true), + "Setting group in buxton failed."); + fail_if(buxton_set_value(c, key, "bxt_test_value", + client_set_value_test, + "group", true), + "Setting value in buxton failed."); + buxton_key_free(group); + buxton_key_free(key); +} +END_TEST + +static void client_set_label_test(BuxtonResponse response, void *data) +{ + BuxtonKey user_key = (BuxtonKey)data; + BuxtonKey key; + char *user_group, *group; + char *user_name, *name; + uid_t uid = getuid(); + char *root_check = getenv(BUXTON_ROOT_CHECK_ENV); + bool skip_check = (root_check && streq(root_check, "0")); + + fail_if(buxton_response_type(response) != BUXTON_CONTROL_SET_LABEL, + "Failed to get set label response type"); + + if (uid == 0) { + fail_if(buxton_response_status(response) != 0, + "Set label failed"); + key = buxton_response_key(response); + fail_if(!key, "Failed to get set label key"); + user_group = buxton_key_get_group(user_key); + fail_if(!user_group, "Failed to get group from user key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group from key"); + fail_if(!streq(group, user_group), + "Incorrect set label group returned"); + free(user_group); + free(group); + + user_name = buxton_key_get_name(user_key); + if (user_name) { + name = buxton_key_get_name(key); + fail_if(!name, "Failed to get name from key"); + fail_if(!streq(name, user_name), + "Incorrect set label name returned"); + free(user_name); + free(name); + } + buxton_key_free(key); + } else { + if (skip_check) { + fail_if(buxton_response_status(response) != 0, + "Set label failed"); + } else { + fail_if(buxton_response_status(response) == 0, + "Set label succeeded, but the client is not root"); + } + } +} +START_TEST(buxton_set_label_check) +{ + BuxtonClient c; + BuxtonKey group = buxton_key_create("bxt_group", NULL, "test-gdbm", STRING); + fail_if(!group, "Failed to create key for group"); + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_create_group(c, group, NULL, NULL, true), + "Creating group in buxton failed."); + fail_if(buxton_set_label(c, group, "*", + client_set_label_test, + group, true), + "Setting label for group in buxton failed."); + + BuxtonKey name = buxton_key_create("bxt_group", "bxt_name", "test-gdbm", STRING); + fail_if(!name, "Failed to create key for name"); + fail_if(buxton_set_value(c, name, "bxt_value", NULL, NULL, true), + "Setting label for name in buxton failed."); + fail_if(buxton_set_label(c, name, "*", + client_set_label_test, + name, true), + "Setting label for name in buxton failed."); + + buxton_key_free(group); + buxton_key_free(name); +} +END_TEST + +static void client_get_value_test(BuxtonResponse response, void *data) +{ + BuxtonKey key; + char *group; + char *name; + char *v; + char *value = (char *)data; + + fail_if(buxton_response_status(response) != 0, + "Get value failed"); + + key = buxton_response_key(response); + fail_if(!key, "Failed to get key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group"); + fail_if(!streq(group, "group"), + "Failed to get correct group"); + name = buxton_key_get_name(key); + fail_if(!name, "Failed to get name"); + fail_if(!streq(name, "name"), + "Failed to get correct name"); + v = buxton_response_value(response); + printf("val=%s\n", v); + fail_if(!v, "Failed to get value"); + fail_if(!streq(v, value), + "Failed to get correct value"); + + free(v); + free(group); + free(name); + buxton_key_free(key); +} +START_TEST(buxton_get_value_for_layer_check) +{ + BuxtonClient c = NULL; + BuxtonKey key = buxton_key_create("group", "name", "test-gdbm-user", STRING); + + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_get_value(c, key, + client_get_value_test, + "bxt_test_value", true), + "Retrieving value from buxton gdbm backend failed."); +} +END_TEST + +START_TEST(buxton_get_value_check) +{ + BuxtonClient c = NULL; + + BuxtonKey group = buxton_key_create("group", NULL, "test-gdbm", STRING); + fail_if(!group, "Failed to create key for group"); + BuxtonKey key = buxton_key_create("group", "name", "test-gdbm", STRING); + + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + + fail_if(buxton_create_group(c, group, NULL, NULL, true), + "Creating group in buxton failed."); + fail_if(buxton_set_label(c, group, "*", NULL, NULL, true), + "Setting group in buxton failed."); + fail_if(buxton_set_value(c, key, "bxt_test_value2", + client_set_value_test, "group", true), + "Failed to set second value."); + buxton_key_free(group); + buxton_key_free(key); + key = buxton_key_create("group", "name", NULL, STRING); + fail_if(buxton_get_value(c, key, + client_get_value_test, + "bxt_test_value2", true), + "Retrieving value from buxton gdbm backend failed."); + buxton_key_free(key); +} +END_TEST + +START_TEST(parse_list_check) +{ + BuxtonData l3[2]; + BuxtonData l2[4]; + BuxtonData l1[3]; + _BuxtonKey key; + BuxtonData *value = NULL; + + fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 2, l1, &key, &value), + "Parsed bad notify argument count"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value), + "Parsed bad notify type 1"); + l1[0].type = STRING; + l1[1].type = FLOAT; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value), + "Parsed bad notify type 3"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value), + "Parsed bad notify type 3"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = UINT32; + l1[0].store.d_string = buxton_string_pack("s1"); + l1[1].store.d_string = buxton_string_pack("s2"); + l1[2].store.d_uint32 = STRING; + fail_if(!parse_list(BUXTON_CONTROL_NOTIFY, 3, l1, &key, &value), + "Unable to parse valid notify"); + fail_if(!streq(key.group.value, l1[0].store.d_string.value), + "Failed to set correct notify group"); + fail_if(!streq(key.name.value, l1[1].store.d_string.value), + "Failed to set correct notify name"); + fail_if(key.type != l1[2].store.d_uint32, + "Failed to set correct notify type"); + + fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 2, l1, &key, &value), + "Parsed bad unnotify argument count"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value), + "Parsed bad unnotify type 1"); + l1[0].type = STRING; + l1[1].type = FLOAT; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value), + "Parsed bad unnotify type 2"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value), + "Parsed bad unnotify type 3"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = UINT32; + l1[0].store.d_string = buxton_string_pack("s3"); + l1[1].store.d_string = buxton_string_pack("s4"); + l1[2].store.d_uint32 = STRING; + fail_if(!parse_list(BUXTON_CONTROL_UNNOTIFY, 3, l1, &key, &value), + "Unable to parse valid unnotify"); + fail_if(!streq(key.group.value, l1[0].store.d_string.value), + "Failed to set correct unnotify group"); + fail_if(!streq(key.name.value, l1[1].store.d_string.value), + "Failed to set correct unnotify name"); + fail_if(key.type != l1[2].store.d_uint32, + "Failed to set correct unnotify type"); + + fail_if(parse_list(BUXTON_CONTROL_GET, 5, l2, &key, &value), + "Parsed bad get argument count"); + l2[0].type = INT32; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value), + "Parsed bad get type 1"); + l2[0].type = STRING; + l2[1].type = FLOAT; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value), + "Parsed bad get type 2"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = BOOLEAN; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value), + "Parsed bad get type 3"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value), + "Parsed bad get type 4"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + l2[0].store.d_string = buxton_string_pack("s5"); + l2[1].store.d_string = buxton_string_pack("s6"); + l2[2].store.d_string = buxton_string_pack("s7"); + l2[3].store.d_uint32 = STRING; + fail_if(!parse_list(BUXTON_CONTROL_GET, 4, l2, &key, &value), + "Unable to parse valid get 1"); + fail_if(!streq(key.layer.value, l2[0].store.d_string.value), + "Failed to set correct get layer 1"); + fail_if(!streq(key.group.value, l2[1].store.d_string.value), + "Failed to set correct get group 1"); + fail_if(!streq(key.name.value, l2[2].store.d_string.value), + "Failed to set correct get name"); + fail_if(key.type != l2[3].store.d_uint32, + "Failed to set correct get type 1"); + l2[0].store.d_string = buxton_string_pack("s6"); + l2[1].store.d_string = buxton_string_pack("s6"); + l2[2].type = UINT32; + l2[2].store.d_uint32 = STRING; + fail_if(!parse_list(BUXTON_CONTROL_GET, 3, l2, &key, &value), + "Unable to parse valid get 2"); + fail_if(!streq(key.group.value, l2[0].store.d_string.value), + "Failed to set correct get group 2"); + fail_if(!streq(key.name.value, l2[1].store.d_string.value), + "Failed to set correct get name 2"); + fail_if(key.type != l2[2].store.d_uint32, + "Failed to set correct get type 2"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value), + "Parsed bad get type 5"); + l1[0].type = STRING; + l1[1].type = FLOAT; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value), + "Parsed bad get type 6"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = BOOLEAN; + fail_if(parse_list(BUXTON_CONTROL_GET, 3, l1, &key, &value), + "Parsed bad get type 7"); + + fail_if(parse_list(BUXTON_CONTROL_SET, 1, l2, &key, &value), + "Parsed bad set argument count"); + l2[0].type = INT32; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = FLOAT; + fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value), + "Parsed bad set type 1"); + l2[0].type = STRING; + l2[1].type = FLOAT; + l2[2].type = STRING; + l2[3].type = FLOAT; + fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value), + "Parsed bad set type 2"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = BOOLEAN; + l2[3].type = FLOAT; + fail_if(parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value), + "Parsed bad set type 3"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = FLOAT; + l2[0].store.d_string = buxton_string_pack("s8"); + l2[1].store.d_string = buxton_string_pack("s9"); + l2[2].store.d_string = buxton_string_pack("s10"); + l2[3].store.d_float = 3.14F; + fail_if(!parse_list(BUXTON_CONTROL_SET, 4, l2, &key, &value), + "Unable to parse valid set 1"); + fail_if(!streq(key.layer.value, l2[0].store.d_string.value), + "Failed to set correct set layer 1"); + fail_if(!streq(key.group.value, l2[1].store.d_string.value), + "Failed to set correct set group 1"); + fail_if(!streq(key.name.value, l2[2].store.d_string.value), + "Failed to set correct set name 1"); + fail_if(value->store.d_float != l2[3].store.d_float, + "Failed to set correct set value 1"); + + fail_if(parse_list(BUXTON_CONTROL_UNSET, 1, l2, &key, &value), + "Parsed bad unset argument count"); + l2[0].type = INT32; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value), + "Parsed bad unset type 1"); + l2[0].type = STRING; + l2[1].type = FLOAT; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value), + "Parsed bad unset type 2"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = BOOLEAN; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value), + "Parsed bad unset type 3"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value), + "Parsed bad unset type 4"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + l2[0].store.d_string = buxton_string_pack("s11"); + l2[1].store.d_string = buxton_string_pack("s12"); + l2[2].store.d_string = buxton_string_pack("s13"); + l2[3].store.d_uint32 = STRING; + fail_if(!parse_list(BUXTON_CONTROL_UNSET, 4, l2, &key, &value), + "Unable to parse valid unset 1"); + fail_if(!streq(key.layer.value, l2[0].store.d_string.value), + "Failed to set correct unset layer 1"); + fail_if(!streq(key.group.value, l2[1].store.d_string.value), + "Failed to set correct unset group 1"); + fail_if(!streq(key.name.value, l2[2].store.d_string.value), + "Failed to set correct unset name 1"); + fail_if(key.type != l2[3].store.d_uint32, + "Failed to set correct unset type 1"); + + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 1, l2, &key, &value), + "Parsed bad set label argument count"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value), + "Parsed bad set label type 1"); + l1[0].type = STRING; + l1[1].type = FLOAT; + l1[2].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value), + "Parsed bad set label type 2"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = BOOLEAN; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value), + "Parsed bad set label type 3"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = STRING; + l1[0].store.d_string = buxton_string_pack("s14"); + l1[1].store.d_string = buxton_string_pack("s15"); + l1[2].store.d_string = buxton_string_pack("*"); + fail_if(!parse_list(BUXTON_CONTROL_SET_LABEL, 3, l1, &key, &value), + "Unable to parse valid set label 1"); + fail_if(!streq(key.layer.value, l1[0].store.d_string.value), + "Failed to set correct set label layer 1"); + fail_if(!streq(key.group.value, l1[1].store.d_string.value), + "Failed to set correct set label group 1"); + fail_if(!streq(value->store.d_string.value, l1[2].store.d_string.value), + "Failed to set correct set label label 1"); + fail_if(key.type != STRING, "Failed to key type in set label"); + l2[0].type = INT32; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value), + "Parsed bad set label type 4"); + l2[0].type = STRING; + l2[1].type = FLOAT; + l2[2].type = STRING; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value), + "Parsed bad set label type 5"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = BOOLEAN; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value), + "Parsed bad set label type 6"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value), + "Parsed bad set label type 7"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = STRING; + l2[0].store.d_string = buxton_string_pack("x1"); + l2[1].store.d_string = buxton_string_pack("x2"); + l2[2].store.d_string = buxton_string_pack("x3"); + l2[3].store.d_string = buxton_string_pack("x4"); + fail_if(!parse_list(BUXTON_CONTROL_SET_LABEL, 4, l2, &key, &value), + "Unable to parse valid set label 2"); + fail_if(!streq(key.layer.value, l2[0].store.d_string.value), + "Failed to set correct set label layer 2"); + fail_if(!streq(key.group.value, l2[1].store.d_string.value), + "Failed to set correct set label group 2"); + fail_if(!streq(key.name.value, l2[2].store.d_string.value), + "Failed to set correct set label name 2"); + fail_if(!streq(value->store.d_string.value, l2[3].store.d_string.value), + "Failed to set correct set label label 2"); + + fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 1, l3, &key, &value), + "Parsed bad create group argument count"); + l3[0].type = INT32; + l3[1].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value), + "Parsed bad create group type 1"); + l3[0].type = STRING; + l3[1].type = FLOAT; + fail_if(parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value), + "Parsed bad create group type 2"); + l3[0].type = STRING; + l3[1].type = STRING; + l3[0].store.d_string = buxton_string_pack("s16"); + l3[1].store.d_string = buxton_string_pack("s17"); + fail_if(!parse_list(BUXTON_CONTROL_CREATE_GROUP, 2, l3, &key, &value), + "Unable to parse valid create group 1"); + fail_if(!streq(key.layer.value, l3[0].store.d_string.value), + "Failed to set correct create group layer 1"); + fail_if(!streq(key.group.value, l3[1].store.d_string.value), + "Failed to set correct create group group 1"); + fail_if(key.type != STRING, "Failed to key type in create group"); + + fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 1, l3, &key, &value), + "Parsed bad remove group argument count"); + l3[0].type = INT32; + l3[1].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value), + "Parsed bad remove group type 1"); + l3[0].type = STRING; + l3[1].type = FLOAT; + fail_if(parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value), + "Parsed bad remove group type 2"); + l3[0].type = STRING; + l3[1].type = STRING; + l3[0].store.d_string = buxton_string_pack("s18"); + l3[1].store.d_string = buxton_string_pack("s19"); + fail_if(!parse_list(BUXTON_CONTROL_REMOVE_GROUP, 2, l3, &key, &value), + "Unable to parse valid remove group 1"); + fail_if(!streq(key.layer.value, l3[0].store.d_string.value), + "Failed to set correct remove group layer 1"); + fail_if(!streq(key.group.value, l3[1].store.d_string.value), + "Failed to set correct remove group group 1"); + fail_if(key.type != STRING, "Failed to key type in remove group"); + + fail_if(parse_list(BUXTON_CONTROL_MIN, 2, l3, &key, &value), + "Parsed bad control type 1"); +} +END_TEST + +START_TEST(create_group_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + client.cred.uid = getuid(); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + server.buxton.client.uid = 0; + + key.layer = buxton_string_pack("test-gdbm-user"); + key.group = buxton_string_pack("daemon-check"); + key.type = STRING; + create_group(&server, &client, &key, &status); + fail_if(status != 0, "Failed to create group"); + + key.layer = buxton_string_pack("test-gdbm"); + create_group(&server, &client, &key, &status); + fail_if(status != 0, "Failed to create group"); + + key.layer = buxton_string_pack("base"); + key.group = buxton_string_pack("tgroup"); + create_group(&server, &client, &key, &status); + fail_if(status != 0, "Failed to create group"); + + buxton_direct_close(&server.buxton); +} +END_TEST + +START_TEST(remove_group_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + client.cred.uid = getuid(); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + server.buxton.client.uid = 0; + + key.layer = buxton_string_pack("base"); + key.group = buxton_string_pack("tgroup"); + key.type = STRING; + + remove_group(&server, &client, &key, &status); + fail_if(status != 0, "Failed to remove group"); + + buxton_direct_close(&server.buxton); +} +END_TEST + +START_TEST(set_label_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + BuxtonData value; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + client.cred.uid = getuid(); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + server.buxton.client.uid = 0; + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("daemon-check"); + key.type = STRING; + value.type = STRING; + value.store.d_string = buxton_string_pack("*"); + + set_label(&server, &client, &key, &value, &status); + fail_if(status != 0, "Failed to set label"); + buxton_direct_close(&server.buxton); +} +END_TEST + +START_TEST(set_value_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + BuxtonData value; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + client.cred.uid = getuid(); + server.buxton.client.uid = 0; + + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + + key.layer = buxton_string_pack("test-gdbm-user"); + key.group = buxton_string_pack("daemon-check"); + key.name = buxton_string_pack("name"); + value.type = STRING; + value.store.d_string = buxton_string_pack("user-layer-value"); + + set_value(&server, &client, &key, &value, &status); + fail_if(status != 0, "Failed to set value"); + fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid"); + + key.layer = buxton_string_pack("test-gdbm"); + value.store.d_string = buxton_string_pack("system-layer-value"); + set_value(&server, &client, &key, &value, &status); + fail_if(status != 0, "Failed to set value"); + + buxton_direct_close(&server.buxton); +} +END_TEST + +START_TEST(get_value_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + BuxtonData *value; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + fail_if(!buxton_cache_smack_rules(), + "Failed to cache smack rules"); + client.cred.uid = getuid(); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + server.buxton.client.uid = 0; + key.layer = buxton_string_pack("test-gdbm-user"); + key.group = buxton_string_pack("daemon-check"); + key.name = buxton_string_pack("name"); + key.type = STRING; + + value = get_value(&server, &client, &key, &status); + fail_if(!value, "Failed to get value"); + fail_if(status != 0, "Failed to get value"); + fail_if(value->type != STRING, "Failed to get correct type"); + fail_if(!streq(value->store.d_string.value, "user-layer-value"), "Failed to get correct value"); + fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid"); + free(value); + + server.buxton.client.uid = 0; + key.layer.value = NULL; + key.layer.length = 0; + value = get_value(&server, &client, &key, &status); + fail_if(!value, "Failed to get value 2"); + fail_if(status != 0, "Failed to get value 2"); + fail_if(value->type != STRING, "Failed to get correct type 2"); + fail_if(!streq(value->store.d_string.value, "system-layer-value"), "Failed to get correct value 2"); + fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid 2"); + free(value); + + buxton_direct_close(&server.buxton); +} +END_TEST + +START_TEST(register_notification_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + client_list_item client, no_client; + BuxtonString clabel = buxton_string_pack("_"); + int32_t status; + BuxtonDaemon server; + uint32_t msgid; + + fail_if(!buxton_cache_smack_rules(), + "Failed to cache smack rules"); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + client.cred.uid = 1002; + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + server.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!server.notify_mapping, "Failed to allocate hashmap"); + + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + key.type = STRING; + register_notification(&server, &client, &key, 1, &status); + fail_if(status != 0, "Failed to register notification"); + register_notification(&server, &client, &key, 1, &status); + fail_if(status != 0, "Failed to register notification"); + //FIXME: Figure out what to do with duplicates + key.group = buxton_string_pack("no-key"); + msgid = unregister_notification(&server, &client, &key, &status); + fail_if(status == 0, + "Unregistered from notifications with invalid key"); + fail_if(msgid != 0, "Got unexpected notify message id"); + key.group = buxton_string_pack("group"); + msgid = unregister_notification(&server, &no_client, &key, &status); + fail_if(status == 0, + "Unregistered from notifications with invalid client"); + fail_if(msgid != 0, "Got unexpected notify message id"); + msgid = unregister_notification(&server, &client, &key, &status); + fail_if(status != 0, + "Unable to unregister from notifications"); + fail_if(msgid != 1, "Failed to get correct notify message id"); + key.group = buxton_string_pack("key2"); + register_notification(&server, &client, &key, 0, &status); + fail_if(status == 0, "Registered notification with key not in db"); + + hashmap_free(server.notify_mapping); + buxton_direct_close(&server.buxton); +} +END_TEST +START_TEST(buxtond_handle_message_error_check) +{ + int client, server; + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1; + client_list_item cl; + bool r; + BuxtonArray *list = NULL; + uint16_t control; + + setup_socket_pair(&client, &server); + fail_if(fcntl(client, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + list = buxton_array_new(); + fail_if(!list, "Failed to allocate list"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + cl.smack_label = &slabel; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + + cl.data = malloc(4); + fail_if(!cl.data, "Couldn't allocate blank message"); + cl.data[0] = 0; + cl.data[1]= 0; + cl.data[2] = 0; + cl.data[3] = 0; + size = 100; + r = buxtond_handle_message(&daemon, &cl, size); + fail_if(r, "Failed to detect invalid message data"); + free(cl.data); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("group"); + r = buxton_array_add(list, &data1); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0, + list); + fail_if(size == 0, "Failed to serialize message"); + control = BUXTON_CONTROL_MIN; + memcpy(cl.data, &control, sizeof(uint16_t)); + r = buxtond_handle_message(&daemon, &cl, size); + fail_if(r, "Failed to detect min control size"); + control = BUXTON_CONTROL_MAX; + memcpy(cl.data, &control, sizeof(uint16_t)); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(r, "Failed to detect max control size"); + + close(client); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&list, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_create_group_check) +{ + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list1, *out_list2; + BuxtonControlMessage msg; + ssize_t csize; + int client, server; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + fail_if(fcntl(client, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + + out_list1 = buxton_array_new(); + fail_if(!out_list1, "Failed to allocate list"); + data1.type = STRING; + data1.store.d_string = buxton_string_pack("base"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("tgroup"); + r = buxton_array_add(out_list1, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list1, &data2); + fail_if(!r, "Failed to add element to array"); + + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_CREATE_GROUP, 0, + out_list1); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to handle create group message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to create group"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to create group"); + fail_if(msgid != 0, "Failed to get correct message id"); + free(list); + + out_list2 = buxton_array_new(); + fail_if(!out_list2, "Failed to allocate list"); + data1.store.d_string = buxton_string_pack("base"); + data2.store.d_string = buxton_string_pack("daemon-check"); + r = buxton_array_add(out_list2, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list2, &data2); + fail_if(!r, "Failed to add element to array"); + + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_CREATE_GROUP, 1, + out_list2); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to handle create group message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to create group"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to create group"); + fail_if(msgid != 1, "Failed to get correct message id"); + + free(list); + cleanup_callbacks(); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list1, NULL); + buxton_array_free(&out_list2, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_remove_group_check) +{ + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonControlMessage msg; + ssize_t csize; + int client, server; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + fail_if(fcntl(client, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("base"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("tgroup"); + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_REMOVE_GROUP, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to handle remove group message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to remove group"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to remove group"); + fail_if(msgid != 0, "Failed to get correct message id"); + + free(list); + cleanup_callbacks(); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_set_label_check) +{ + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonControlMessage msg; + ssize_t csize; + int client, server; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + fail_if(fcntl(client, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("base"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("*"); + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_SET_LABEL, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to handle set label message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to set label"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to set label"); + fail_if(msgid != 0, "Failed to get correct message id"); + + free(list); + cleanup_callbacks(); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_set_value_check) +{ + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3, data4; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonControlMessage msg; + ssize_t csize; + int client, server; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + fail_if(fcntl(client, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("base"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("name"); + data4.type = STRING; + data4.store.d_string = buxton_string_pack("bxt_test_value3"); + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data4); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(r, "Failed to detect parse_list failure"); + + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_SET, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to handle set message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to set"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to set"); + fail_if(msgid != 0, "Failed to get correct message id"); + + free(list); + cleanup_callbacks(); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_get_check) +{ + int client, server; + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3, data4; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonArray *out_list2; + BuxtonControlMessage msg; + ssize_t csize; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = getuid(); + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("test-gdbm-user"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("name"); + data4.type = UINT32; + data4.store.d_uint32 = STRING; + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data4); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to get message 1"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 2, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != INT32, "Failed to get correct response type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to get value"); + fail_if(list[1].type != STRING, "Failed to get correct value type"); + fail_if(!streq(list[1].store.d_string.value, "user-layer-value"), + "Failed to get correct value"); + + free(list[1].store.d_string.value); + free(list); + + out_list2 = buxton_array_new(); + fail_if(!out_list2, "Failed to allocate list 2"); + r = buxton_array_add(out_list2, &data2); + fail_if(!r, "Failed to add element to array 2"); + r = buxton_array_add(out_list2, &data3); + fail_if(!r, "Failed to add element to array 2"); + r = buxton_array_add(out_list2, &data4); + fail_if(!r, "Failed to add element to array 2"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET, 0, + out_list2); + fail_if(size == 0, "Failed to serialize message 2"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to get message 2"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed 2"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 2, "Failed to get correct response to get 2"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type 2"); + fail_if(msgid != 0, "Failed to get correct message id 2"); + fail_if(list[0].type != INT32, "Failed to get correct response type 2"); + fail_if(list[0].store.d_int32 != 0, + "Failed to get value 2"); + fail_if(list[1].type != STRING, "Failed to get correct value type 2"); + fail_if(streq(list[1].store.d_string.value, "bxt_test_value2"), + "Failed to get correct value 2"); + + free(list[1].store.d_string.value); + free(list); + close(client); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); + buxton_array_free(&out_list2, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_notify_check) +{ + int client, server; + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonControlMessage msg; + ssize_t csize; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("group"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("name"); + data3.type = UINT32; + data3.store.d_uint32 = STRING; + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_NOTIFY, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to register for notification"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to notify"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct notify message id"); + fail_if(list[0].type != INT32, "Failed to get correct response type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to register notification"); + + free(list); + + /* UNNOTIFY */ + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_UNNOTIFY, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to unregister from notification"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed 2"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 2, "Failed to get correct response to unnotify"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type 2"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type 2"); + fail_if(list[0].store.d_int32 != 0, + "Failed to unregister for notification"); + fail_if(list[1].type != UINT32, + "Failed to get correct unnotify message id type"); + fail_if(list[1].store.d_uint32 != 0, + "Failed to get correct unnotify message id"); + fail_if(msgid != 0, "Failed to get correct message id 2"); + + free(list); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); +} +END_TEST + +START_TEST(buxtond_handle_message_unset_check) +{ + int client, server; + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3, data4; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonControlMessage msg; + ssize_t csize; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + daemon.notify_mapping = hashmap_new(string_hash_func, string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("base"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("name"); + data4.type = UINT32; + data4.store.d_uint32 = STRING; + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data4); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_UNSET, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to unset message"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, "Failed to get correct response to unset"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(list[0].type != INT32, + "Failed to get correct indicator type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to unset"); + fail_if(msgid != 0, "Failed to get correct message id"); + + free(list); + close(client); + hashmap_free(daemon.notify_mapping); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); +} +END_TEST + +START_TEST(buxtond_notify_clients_check) +{ + int client, server; + BuxtonDaemon daemon; + _BuxtonKey key; + BuxtonString slabel; + BuxtonData value1, value2; + client_list_item cl; + int32_t status; + bool r; + BuxtonData *list; + BuxtonControlMessage msg; + ssize_t csize; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = 1002; + daemon.notify_mapping = hashmap_new(string_hash_func, + string_compare_func); + fail_if(!daemon.notify_mapping, "Failed to allocate hashmap"); + fail_if(!buxton_cache_smack_rules(), + "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + + value1.type = STRING; + value1.store.d_string = buxton_string_pack("dummy value"); + key.group = buxton_string_pack("dummy"); + key.name = buxton_string_pack("name"); + buxtond_notify_clients(&daemon, &cl, &key, &value1); + + value1.store.d_string = buxton_string_pack("real value"); + key.group = buxton_string_pack("daemon-check"); + key.name = buxton_string_pack("name"); + key.layer = buxton_string_pack("base"); + key.type = STRING; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value1); + + value2.type = STRING; + value2.store.d_string = buxton_string_pack("new value"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify string"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != STRING, + "Failed to get correct notification value type string"); + fail_if(!streq(list[0].store.d_string.value, "new value"), + "Failed to get correct notification value data string"); + + free(list[0].store.d_string.value); + free(list); + + key.group = buxton_string_pack("group"); + key.name.value = NULL; + key.name.length = 0; + r = buxton_direct_create_group(&daemon.buxton, &key, NULL); + fail_if(!r, "Unable to create group"); + r = buxton_direct_set_label(&daemon.buxton, &key, &slabel); + fail_if(!r, "Unable set group label"); + + value1.type = INT32; + value1.store.d_int32 = 1; + value2.type = INT32; + value2.store.d_int32 = 2; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name32"); + key.type = INT32; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify int32"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != INT32, + "Failed to get correct notification value type int32"); + fail_if(list[0].store.d_int32 != 2, + "Failed to get correct notification value data int32"); + + free(list); + + value1.type = UINT32; + value1.store.d_uint32 = 1; + value2.type = UINT32; + value2.store.d_uint32 = 2; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("nameu32"); + key.type = UINT32; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify uint32"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != UINT32, + "Failed to get correct notification value type uint32"); + fail_if(list[0].store.d_uint32 != 2, + "Failed to get correct notification value data uint32"); + + free(list); + + value1.type = INT64; + value1.store.d_int64 = 2; + value2.type = INT64; + value2.store.d_int64 = 3; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name64"); + key.type = INT64; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify int64"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != INT64, + "Failed to get correct notification value type int 64"); + fail_if(list[0].store.d_int64 != 3, + "Failed to get correct notification value data int64"); + + free(list); + + value1.type = UINT64; + value1.store.d_uint64 = 2; + value2.type = UINT64; + value2.store.d_uint64 = 3; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("nameu64"); + key.type = UINT64; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify uint64"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != UINT64, + "Failed to get correct notification value type uint64"); + fail_if(list[0].store.d_uint64 != 3, + "Failed to get correct notification value data uint64"); + + free(list); + + value1.type = FLOAT; + value1.store.d_float = 3.1F; + value2.type = FLOAT; + value2.store.d_float = 3.14F; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("namef"); + key.type = FLOAT; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify float"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != FLOAT, + "Failed to get correct notification value type float"); + fail_if(list[0].store.d_float != 3.14F, + "Failed to get correct notification value data float"); + + free(list); + + value1.type = DOUBLE; + value1.store.d_double = 3.141F; + value2.type = DOUBLE; + value2.store.d_double = 3.1415F; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("named"); + key.type = DOUBLE; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify double"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != DOUBLE, + "Failed to get correct notification value type double"); + fail_if(list[0].store.d_double != 3.1415F, + "Failed to get correct notification value data double"); + + free(list); + + value1.type = BOOLEAN; + value1.store.d_boolean = false; + value2.type = BOOLEAN; + value2.store.d_int32 = true; + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("nameb"); + key.type = BOOLEAN; + r = buxton_direct_set_value(&daemon.buxton, &key, + &value1, NULL); + fail_if(!r, "Failed to set value for notify"); + register_notification(&daemon, &cl, &key, 0, &status); + fail_if(status != 0, + "Failed to register notification for notify"); + buxtond_notify_clients(&daemon, &cl, &key, &value2); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 1, + "Failed to get correct response to notify bool"); + fail_if(msg != BUXTON_CONTROL_CHANGED, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != BOOLEAN, + "Failed to get correct notification value type bool"); + fail_if(list[0].store.d_boolean != true, + "Failed to get correct notification value data bool"); + + free(list); + close(client); + buxton_direct_close(&daemon.buxton); +} +END_TEST + +START_TEST(identify_client_check) +{ + int sender; + client_list_item client; + bool r; + int32_t msg = 5; + + setup_socket_pair(&client.fd, &sender); + r = identify_client(&client); + fail_if(r, "Identified client without message"); + + write(sender, &msg, sizeof(int32_t)); + r = identify_client(&client); + fail_if(!r, "Identify client failed"); + + close(client.fd); + close(sender); +} +END_TEST + +START_TEST(add_pollfd_check) +{ + BuxtonDaemon daemon; + int fd; + short events; + bool a; + + fd = 3; + daemon.nfds_alloc = 0; + daemon.accepting_alloc = 0; + daemon.nfds = 0; + daemon.pollfds = NULL; + daemon.accepting = NULL; + events = 1; + a = true; + add_pollfd(&daemon, fd, events, a); + fail_if(daemon.nfds != 1, "Failed to increase nfds"); + fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd"); + fail_if(daemon.pollfds[0].events != events, "Failed to set events"); + fail_if(daemon.pollfds[0].revents != 0, "Failed to set revents"); + fail_if(daemon.accepting[0] != a, "Failed to set accepting status"); + free(daemon.pollfds); + free(daemon.accepting); +} +END_TEST + +START_TEST(del_pollfd_check) +{ + BuxtonDaemon daemon; + int fd; + short events; + bool a; + + fd = 3; + daemon.nfds_alloc = 0; + daemon.accepting_alloc = 0; + daemon.nfds = 0; + daemon.pollfds = NULL; + daemon.accepting = NULL; + events = 1; + a = true; + add_pollfd(&daemon, fd, events, a); + fail_if(daemon.nfds != 1, "Failed to add pollfd"); + del_pollfd(&daemon, 0); + fail_if(daemon.nfds != 0, "Failed to decrease nfds 1"); + + fd = 4; + events = 2; + a = false; + add_pollfd(&daemon, fd, events, a); + fail_if(daemon.nfds != 1, "Failed to increase nfds after del"); + fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd after del"); + fail_if(daemon.pollfds[0].events != events, + "Failed to set events after del"); + fail_if(daemon.pollfds[0].revents != 0, + "Failed to set revents after del"); + fail_if(daemon.accepting[0] != a, + "Failed to set accepting status after del"); + fd = 5; + events = 3; + a = true; + add_pollfd(&daemon, fd, events, a); + del_pollfd(&daemon, 0); + fail_if(daemon.nfds != 1, "Failed to delete fd 2"); + fail_if(daemon.pollfds[0].fd != fd, "Failed to set pollfd after del2"); + fail_if(daemon.pollfds[0].events != events, + "Failed to set events after del2"); + fail_if(daemon.pollfds[0].revents != 0, + "Failed to set revents after del2"); + fail_if(daemon.accepting[0] != a, + "Failed to set accepting status after del2"); +} +END_TEST + +START_TEST(handle_smack_label_check) +{ + client_list_item client; + int server; + + setup_socket_pair(&client.fd, &server); + handle_smack_label(&client); + + close(client.fd); + close(server); +} +END_TEST + +START_TEST(terminate_client_check) +{ + client_list_item *client; + BuxtonDaemon daemon; + int dummy; + + client = malloc0(sizeof(client_list_item)); + fail_if(!client, "client malloc failed"); + client->smack_label = malloc0(sizeof(BuxtonString)); + fail_if(!client->smack_label, "smack label malloc failed"); + daemon.client_list = client; + setup_socket_pair(&client->fd, &dummy); + daemon.nfds_alloc = 0; + daemon.accepting_alloc = 0; + daemon.nfds = 0; + daemon.pollfds = NULL; + daemon.accepting = NULL; + add_pollfd(&daemon, client->fd, 2, false); + fail_if(daemon.nfds != 1, "Failed to add pollfd"); + client->smack_label->value = strdup("dummy"); + client->smack_label->length = 6; + fail_if(!client->smack_label->value, "label strdup failed"); + + terminate_client(&daemon, client, 0); + fail_if(daemon.client_list, "Failed to set client list item to NULL"); + close(dummy); +} +END_TEST + +START_TEST(handle_client_check) +{ + BuxtonDaemon daemon; + int dummy; + uint8_t buf[4096]; + uint8_t *message = NULL; + BuxtonData data1, data2, data3, data4; + BuxtonArray *list = NULL; + bool r; + size_t ret; + uint32_t bsize; + + list = buxton_array_new(); + data1.type = STRING; + data1.store.d_string = buxton_string_pack("test-gdbm-user"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("name"); + data4.type = UINT32; + data4.store.d_uint32 = STRING; + r = buxton_array_add(list, &data1); + fail_if(!r, "Failed to add data to array"); + r = buxton_array_add(list, &data2); + fail_if(!r, "Failed to add data to array"); + r = buxton_array_add(list, &data3); + fail_if(!r, "Failed to add data to array"); + r = buxton_array_add(list, &data4); + fail_if(!r, "Failed to add data to array"); + ret = buxton_serialize_message(&message, BUXTON_CONTROL_GET, 0, list); + fail_if(ret == 0, "Failed to serialize string data"); + daemon.client_list = malloc0(sizeof(client_list_item)); + fail_if(!daemon.client_list, "client malloc failed"); + setup_socket_pair(&daemon.client_list->fd, &dummy); + fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); + daemon.nfds_alloc = 0; + daemon.accepting_alloc = 0; + daemon.nfds = 0; + daemon.pollfds = NULL; + daemon.accepting = NULL; + add_pollfd(&daemon, daemon.client_list->fd, 2, false); + fail_if(daemon.nfds != 1, "Failed to add pollfd 1"); + fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 1"); + fail_if(daemon.client_list, "Failed to terminate client with no data"); + close(dummy); + + daemon.client_list = malloc0(sizeof(client_list_item)); + fail_if(!daemon.client_list, "client malloc failed"); + setup_socket_pair(&daemon.client_list->fd, &dummy); + fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); + add_pollfd(&daemon, daemon.client_list->fd, 2, false); + fail_if(daemon.nfds != 1, "Failed to add pollfd 2"); + write(dummy, buf, 1); + fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 2"); + fail_if(!daemon.client_list, "Terminated client with insufficient data"); + fail_if(daemon.client_list->data, "Didn't clean up left over client data 1"); + + bsize = 0; + memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t)); + write(dummy, message, BUXTON_MESSAGE_HEADER_LENGTH); + fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 3"); + fail_if(daemon.client_list, "Failed to terminate client with bad size 1"); + close(dummy); + + daemon.client_list = malloc0(sizeof(client_list_item)); + fail_if(!daemon.client_list, "client malloc failed"); + setup_socket_pair(&daemon.client_list->fd, &dummy); + fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); + add_pollfd(&daemon, daemon.client_list->fd, 2, false); + fail_if(daemon.nfds != 1, "Failed to add pollfd 3"); + bsize = BUXTON_MESSAGE_MAX_LENGTH + 1; + memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t)); + write(dummy, message, BUXTON_MESSAGE_HEADER_LENGTH); + fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 4"); + fail_if(daemon.client_list, "Failed to terminate client with bad size 2"); + close(dummy); + + daemon.client_list = malloc0(sizeof(client_list_item)); + fail_if(!daemon.client_list, "client malloc failed"); + setup_socket_pair(&daemon.client_list->fd, &dummy); + fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); + add_pollfd(&daemon, daemon.client_list->fd, 2, false); + fail_if(daemon.nfds != 1, "Failed to add pollfd 4"); + bsize = (uint32_t)ret; + memcpy(message + BUXTON_LENGTH_OFFSET, &bsize, sizeof(uint32_t)); + write(dummy, message, ret); + fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 5"); + fail_if(!daemon.client_list, "Terminated client with correct data length"); + + for (int i = 0; i < 33; i++) { + write(dummy, message, ret); + } + fail_if(!handle_client(&daemon, daemon.client_list, 0), "No more data available"); + fail_if(!daemon.client_list, "Terminated client with correct data length"); + terminate_client(&daemon, daemon.client_list, 0); + fail_if(daemon.client_list, "Failed to remove client 1"); + close(dummy); + + //FIXME add SIGPIPE handler + /* daemon.client_list = malloc0(sizeof(client_list_item)); */ + /* fail_if(!daemon.client_list, "client malloc failed"); */ + /* setup_socket_pair(&daemon.client_list->fd, &dummy); */ + /* fcntl(daemon.client_list->fd, F_SETFL, O_NONBLOCK); */ + /* add_pollfd(&daemon, daemon.client_list->fd, 2, false); */ + /* fail_if(daemon.nfds != 1, "Failed to add pollfd 5"); */ + /* write(dummy, message, ret); */ + /* close(dummy); */ + /* fail_if(handle_client(&daemon, daemon.client_list, 0), "More data available 6"); */ + /* fail_if(daemon.client_list, "Failed to terminate client"); */ +} +END_TEST + +START_TEST(buxtond_eat_garbage_check) +{ + daemon_pid = 0; + sigset_t sigset; + pid_t pid; + + unlink(buxton_socket()); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + pid = fork(); + fail_if(pid < 0, "couldn't fork"); + if (pid) { /* parent*/ + BuxtonClient c; + FuzzContext fuzz; + time_t start; + bool keep_going = true; + int fd; + + + srand(0); + bzero(&fuzz, sizeof(FuzzContext)); + + daemon_pid = pid; + usleep(250*1000); + check_did_not_crash(daemon_pid, &fuzz); + + + fail_if(time(&start) == -1, "call to time() failed"); + do { + pid_t client; + ssize_t bytes; + time_t now; + + fail_if(time(&now) == -1, "call to time() failed"); + if (now - start >= fuzz_time) { + keep_going = false; + } + + fuzz.size = (unsigned int)rand() % 4096; + for (int i=0; i < fuzz.size; i++) { + fuzz.buf[i] = (uint8_t)(rand() % 255); + } + if ((fuzz.size >= 6) && (rand() % 4096)) { + uint16_t control = (uint16_t)((rand() % (BUXTON_CONTROL_MAX-1)) + 1); + + /* magic */ + fuzz.buf[0] = 0x06; + fuzz.buf[1] = 0x72; + + /* valid message type */ + memcpy((void*)(fuzz.buf + 2), (void*)(&control), sizeof(uint16_t)); + + /* valid size */ + memcpy((void *)(fuzz.buf + 4), (void *)(&fuzz.size), sizeof(uint32_t)); + } + client = fork(); + fail_if(client == -1, "couldn't fork"); + if (client == 0) { + fd = buxton_open(&c); + fail_if(fd == -1, + "Open failed with daemon%s", dump_fuzz(&fuzz)); + + + bytes = write(fd, (void*)(fuzz.buf), fuzz.size); + fail_if(bytes == -1, "write failed: %m%s", dump_fuzz(&fuzz)); + fail_unless(bytes == fuzz.size, "write was %d instead of %d", bytes, fuzz.size); + + buxton_close(c); + usleep(1*1000); + + check_did_not_crash(daemon_pid, &fuzz); + exit(0); + } else { + int status; + pid_t wait = waitpid(client, &status, 0); + fail_if(wait == -1, "waitpid failed"); + fail_unless(WIFEXITED(status), "client died"); + fuzz.iteration++; + } + } while (keep_going); + } else { /* child */ + exec_daemon(); + } +} +END_TEST + +BuxtonClient c; + +void cleanup(void) +{ + //used to cleanup the helper groups, values, names that are needed by other fuzzed values/labels + + fail_if(buxton_open(&c) == -1, "Cleanup: Open failed with daemon."); + + BuxtonKey key = buxton_key_create("tempgroup", NULL, "base", STRING); + fail_if(buxton_remove_group(c, key, NULL, "tempgroup", true), "Cleanup: Error at removing"); + buxton_key_free(key); + buxton_close(c); + +} + +char *random_string(int str_size) +{ + //generates random strings of maximum str_size characters + //ignore this for now, it will be replaced by something more intelligent + + int size; + char *str; + + size = rand() % str_size; + str = calloc((size_t)(size + 1), sizeof(char)); + + for (int i = 0; i < size ; i++) { + str[i] = (char)(rand() % 255 + 1); //1-255, use % 25+97 to use lower-case alpha chars + } + + return str; +} + +static void SIGPIPE_handler(int signo) +{ + buxton_close(c); + + //reconnect + fail_if(buxton_open(&c) == -1,"SIGPIPE: Open failed with daemon."); +} + +START_TEST(buxtond_fuzz_commands) +{ +/* Previous fuzzer had correct MAGIC, CONTROL code, Message SIZE and randomized everything else. + * This only randomizes the Data Value. + * + * If you want to fuzz the protocol, use the above fuzzer. + * If you want to fuzz the daemon, use this one. Use export BUXTON_FUZZER=NEW + */ + + daemon_pid = 0; + pid_t pid; + sigset_t sigset; + struct sigaction sa; + char *random_group, *random_layer, *random_label, *random_value, *random_name; + int max_length = 32768; + unlink(buxton_socket()); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + // since the daemon will close the connection with the client if it receives a "weird" command + // we have to treat SIGPIPE + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIGPIPE_handler; + sigaction(SIGPIPE, &sa, NULL); + + printf("============== CAUTION!!! Fuzzer at work =================\n"); + FILE *f = fopen("debug_check_daemon.txt", "w"); + fclose(f); + + pid = fork(); + fail_if(pid < 0, "couldn't fork"); + if (pid) { /* parent*/ + FuzzContext fuzz; + time_t start; + bool keep_going = true; + + srand((unsigned int) time(NULL)); + bzero(&fuzz, sizeof(FuzzContext)); + + usleep(250*1000); //wait for daemon to start + + fail_if(time(&start) == -1, "call to time() failed"); + do { + BuxtonKey key = NULL; + time_t now; + + fail_if(time(&now) == -1, "call to time() failed"); + if (now - start >= fuzz_time) { + keep_going = false; + } + + cleanup(); + + /* create a random group and layer */ + random_group = random_string(max_length); + random_layer = random_string(max_length); + + key = buxton_key_create(random_group, NULL, random_layer, STRING); + fail_if(!key, "Failed to create key"); + fail_if(buxton_open(&c) == -1, "Open failed with daemon."); + + f = fopen("debug_check_daemon.txt", "w"); + fail_if(!f, "Unable to open file\n"); + fprintf(f, "Create group: Group: %s\t Layer: %s\n", random_group, random_layer); + fflush(f); + + if (buxton_create_group(c, key, NULL, random_group, true)) { + fprintf(f, "1: Group created!\n"); + } else { + fprintf(f, "1: Group was NOT created.\n"); + } + fflush(f); + buxton_key_free(key); + + /* create a random group in an existing layer */ + key = buxton_key_create(random_group, NULL, "base", STRING); + fail_if(!key, "Failed to create key"); + fprintf(f, "Create group: Group: %s\t Layer: base\n", random_group); + fflush(f); + + if (buxton_create_group(c, key, NULL, random_group, true)) { + fprintf(f, "1: Group created!\n"); + } else { + fprintf(f, "1: Group was NOT created.\n"); + } + fflush(f); + buxton_key_free(key); + + // create a random name on random group on a random layer + random_name = random_string(max_length); + key = buxton_key_create(random_group, random_name, random_layer, STRING); + fail_if(!key, "Failed to create key"); + + fprintf(f, "Create name: Group: %s\t Layer: %s\t Name:%s\n", random_group, random_layer, random_name); + fflush(f); + + if (buxton_create_group(c, key, NULL, random_group, true)) { + fprintf(f, "2: Name created!\n"); + } else { + fprintf(f, "2: Name was NOT created.\n"); + } + fflush(f); + buxton_key_free(key); + + // create a random name on existing group + // create group + key = buxton_key_create("tempgroup", NULL, "base", STRING); + fail_if(!key, "Failed to create key"); + fail_if(buxton_create_group(c, key, NULL,"tempgroup", true), "Creating group in buxton failed."); + buxton_key_free(key); + + // put name on group + key = buxton_key_create("tempgroup", random_name, "base", STRING); + fail_if(!key, "Failed to create key"); + fprintf(f, "Create name: Group: tempgroup\t Layer: base\t Name: %s\n", random_name); + + if (buxton_create_group(c, key, NULL, "tempgroup", true)) { + fprintf(f, "2: Name created!\n"); + } else { + fprintf(f, "2: Name was NOT created.\n"); + } + fflush(f); + buxton_key_free(key); + + // create a random value on a existing labeled group + //create the "existing" group, plus the name + random_value = random_string(max_length); + BuxtonKey group = buxton_key_create("tempgroup", NULL, "base", STRING); + fail_if(!group, "Failed to create key for group"); + key = buxton_key_create("tempgroup", "name", "base", STRING); + fail_if(!key, "Failed to create key"); + + // set the a correct label and randomized value + fprintf(f, "Set label: Group: tgroup\t Layer: base\t Value: %s\n", random_value); + fflush(f); + fail_if(buxton_set_label(c, group, "*", NULL, NULL, true), "Setting label in buxton failed."); + + if (buxton_set_value(c, key, random_value, NULL, "tgroup", true)) { + fprintf(f, "3: Value was set!\n"); + } else { + fprintf(f, "3: Value was NOT set.\n"); + } + fflush(f); + buxton_key_free(group); + buxton_key_free(key); + + //set a random label on an existing group + random_label = random_string(3); + group = buxton_key_create("tempgroup", NULL, "base", STRING); + fail_if(!group, "Failed to create key for group"); + + fprintf(f, "Set label: Group: tempgroup\t Layer: base\t Label: %s\n", random_label); + if (buxton_set_label(c, group, random_label, NULL, group, true)) { + fprintf(f, "3: Label was set!\n"); + } else { + fprintf(f, "3: Label was NOT set.\n"); + } + fflush(f); + + //set random value/label on name + BuxtonKey name = buxton_key_create("tempgroup", "name", "base", STRING); + fail_if(!name, "Failed to create key for name"); + + fprintf(f, "Set label and value: Group: tempgroup\t Layer: base\t Name: name\t Value: %s\t Label: %s \n", random_value, random_label); + if (buxton_set_value(c, name, random_value, NULL, NULL, true)) { + fprintf(f, "4: Value on name was set!\n"); + } else { + fprintf(f, "4: Value on name was NOT set.\n"); + } + free(random_value); + fflush(f); + + if (buxton_set_label(c, name, random_label, NULL, name, true)) { + fprintf(f, "4: Label on name was set!\n"); + } else { + fprintf(f, "4: Label on name was NOT set.\n"); + } + fflush(f); + buxton_key_free(group); + buxton_key_free(name); + free(random_label); + + // remove name from group + key = buxton_key_create(random_group, random_name, random_layer, STRING); + fprintf(f, "Remove group: Group: %s\t Layer: %s\t Name:%s\n", random_group, random_layer, random_name); + if (buxton_remove_group(c, key, NULL, random_group, true)) { + fprintf(f, "5: Name from group was removed!\n"); + } else { + fprintf(f, "5: Name from group was NOT removed.\n"); + } + fflush(f); + buxton_key_free(key); + + // remove name from existing group + key = buxton_key_create("tempgroup", random_name, "base", STRING); + fprintf(f, "Remove group: Group: tempgroup\t Layer: base\t Name:%s\n", random_name); + if (buxton_remove_group(c, key, NULL, "tempgroup", true)) { + fprintf(f, "5: Name from group was removed!\n"); + } else { + fprintf(f, "5: Name from group was NOT removed.\n"); + } + fflush(f); + free(random_name); + buxton_key_free(key); + + /* remove group from existing layer*/ + key = buxton_key_create(random_group, NULL, "base", STRING); + fail_if(!key, "Failed to create key"); + fprintf(f, "Remove group: Group: %s\t Layer: base\n", random_group); + if (buxton_remove_group(c, key, NULL, random_group, true)) { + fprintf(f, "5: Group was removed!\n"); + } else { + fprintf(f, "5: Group was NOT removed.\n"); + } + fflush(f); + buxton_key_free(key); + + /* remove group */ + key = buxton_key_create(random_group, NULL, random_layer, STRING); + fail_if(!key, "Failed to create key"); + fprintf(f, "Remove group: Group: %s\t Layer: %s\n", random_group, random_layer); + if (buxton_remove_group(c, key, NULL, random_group, true)) { + fprintf(f, "5: Group was removed!\n"); + } else { + fprintf(f, "5: Group was NOT removed.\n"); + } + buxton_key_free(key); + fflush(f); + + buxton_close(c); + usleep(1*1000); + + fprintf(f, "5: Closed comm.\n"); + fclose(f); + free(random_layer); + free(random_group); + + reap_callbacks(); + } while (keep_going); + } else { /* child */ + exec_daemon(); + } + + usleep(3 * 1000); + reap_callbacks(); +} +END_TEST + +static Suite * +daemon_suite(void) +{ + Suite *s; + TCase *tc; + char *fuzzer_engine; + + s = suite_create("daemon"); + tc = tcase_create("daemon test functions"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, buxton_open_check); + tcase_add_test(tc, buxton_create_group_check); + tcase_add_test(tc, buxton_remove_group_check); + tcase_add_test(tc, buxton_set_value_check); + tcase_add_test(tc, buxton_set_label_check); + tcase_add_test(tc, buxton_get_value_for_layer_check); + tcase_add_test(tc, buxton_get_value_check); + suite_add_tcase(s, tc); + + tc = tcase_create("buxton_daemon_functions"); + tcase_add_test(tc, parse_list_check); + tcase_add_test(tc, create_group_check); + tcase_add_test(tc, remove_group_check); + tcase_add_test(tc, set_label_check); + + tcase_add_test(tc, set_value_check); + tcase_add_test(tc, get_value_check); + tcase_add_test(tc, register_notification_check); + tcase_add_test(tc, buxtond_handle_message_error_check); + tcase_add_test(tc, buxtond_handle_message_create_group_check); + tcase_add_test(tc, buxtond_handle_message_remove_group_check); + tcase_add_test(tc, buxtond_handle_message_set_label_check); + tcase_add_test(tc, buxtond_handle_message_set_value_check); + tcase_add_test(tc, buxtond_handle_message_get_check); + tcase_add_test(tc, buxtond_handle_message_notify_check); + tcase_add_test(tc, buxtond_handle_message_unset_check); + tcase_add_test(tc, buxtond_notify_clients_check); + tcase_add_test(tc, identify_client_check); + tcase_add_test(tc, add_pollfd_check); + tcase_add_test(tc, del_pollfd_check); + tcase_add_test(tc, handle_smack_label_check); + tcase_add_test(tc, terminate_client_check); + tcase_add_test(tc, handle_client_check); + suite_add_tcase(s, tc); + + tc = tcase_create("buxton daemon evil tests"); + tcase_add_checked_fixture(tc, NULL, teardown); + fuzzer_engine = getenv("BUXTON_FUZZER"); + if (fuzzer_engine) { + if (strcmp(fuzzer_engine,"NEW") == 0) { + tcase_add_test(tc, buxtond_fuzz_commands); + } else { + tcase_add_test(tc, buxtond_eat_garbage_check); + } + } else { + tcase_add_test(tc, buxtond_eat_garbage_check); + } + tcase_set_timeout(tc, fuzz_time+2); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + char *fuzzenv; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf"); + putenv("BUXTON_ROOT_CHECK=0"); + fuzzenv = getenv("BUXTON_FUZZ_TIME"); + if (fuzzenv) { + fuzz_time = atoi(fuzzenv); + } else { + fuzz_time = 2; + } + s = daemon_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_shared_lib.c b/test/check_shared_lib.c new file mode 100644 index 0000000..cb257c1 --- /dev/null +++ b/test/check_shared_lib.c @@ -0,0 +1,894 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = ""; + char *head2 = ""; + char *data = ""; + + /* 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: + */ diff --git a/test/check_smack.c b/test/check_smack.c new file mode 100644 index 0000000..107c4f1 --- /dev/null +++ b/test/check_smack.c @@ -0,0 +1,228 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buxton.h" +#include "configurator.h" +#include "check_utils.h" +#include "daemon.h" +#include "log.h" +#include "smack.h" +#include "util.h" + +#ifdef NDEBUG + #error "re-run configure with --enable-debug" +#endif + +static pid_t daemon_pid; + +static void exec_daemon(void) +{ + char path[PATH_MAX]; + + //FIXME: path is wrong for makedistcheck + snprintf(path, PATH_MAX, "%s/check_buxtond", get_current_dir_name()); + + if (execl(path, "check_buxtond", (const char*)NULL) < 0) { + fail("couldn't exec: %m"); + } + fail("should never reach here"); +} + +static void setup(void) +{ + daemon_pid = 0; + sigset_t sigset; + pid_t pid; + + unlink(buxton_socket()); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + pid = fork(); + fail_if(pid < 0, "couldn't fork"); + if (pid) { + /* parent*/ + daemon_pid = pid; + usleep(128*1000); + } else { + /* child */ + exec_daemon(); + } +} + +static void teardown(void) +{ + if (daemon_pid) { + int status; + pid_t pid; + + pid = waitpid(daemon_pid, &status, WNOHANG); + fail_if(pid == -1, "waitpid error"); + if (pid) { + fail("daemon crashed!"); + } else { + /* if the daemon is still running, kill it */ + kill(SIGTERM, daemon_pid); + usleep(64*1000); + kill(SIGKILL, daemon_pid); + } + } +} + +START_TEST(smack_access_check) +{ + bool ret; + BuxtonString subject; + BuxtonString object; + + ret = buxton_cache_smack_rules(); + fail_if(!ret, "Failed to cache Smack rules"); + + subject = buxton_string_pack("system"); + object = buxton_string_pack("base/sample/key"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access was denied, but should have been granted"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(ret, "Write access was granted, but should have been denied"); + + subject = buxton_string_pack("system"); + object = buxton_string_pack("system/sample/key"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access was denied"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(!ret, "Write access was denied"); + + subject = buxton_string_pack("*"); + object = buxton_string_pack("foo"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(ret, "Read access granted for * subject"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(ret, "Write access granted for * subject"); + + subject = buxton_string_pack("foo"); + object = buxton_string_pack("@"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for @ object"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(!ret, "Write access denied for @ object"); + + subject = buxton_string_pack("@"); + object = buxton_string_pack("foo"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for @ subject"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(!ret, "Write access denied for @ subject"); + + subject = buxton_string_pack("foo"); + object = buxton_string_pack("*"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for * object"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(!ret, "Write access denied for * object"); + + subject = buxton_string_pack("foo"); + object = buxton_string_pack("foo"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for matching subject/object"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(!ret, "Write access denied for matching subject/object"); + + subject = buxton_string_pack("foo"); + object = buxton_string_pack("_"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for _ object"); + + subject = buxton_string_pack("^"); + object = buxton_string_pack("foo"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(!ret, "Read access denied for ^ subject"); + + subject = buxton_string_pack("subjecttest"); + object = buxton_string_pack("objecttest"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_READ); + fail_if(ret, "Read access granted for unrecognized subject/object"); + + subject = buxton_string_pack("subjecttest"); + object = buxton_string_pack("objecttest"); + ret = buxton_check_smack_access(&subject, &object, ACCESS_WRITE); + fail_if(ret, "Write access granted for unrecognized subject/object"); +} +END_TEST + +static Suite * +daemon_suite(void) +{ + Suite *s; + TCase *tc; + bool __attribute__((unused))dummy; + + s = suite_create("smack"); + + dummy = buxton_cache_smack_rules(); + if (buxton_smack_enabled()) { + tc = tcase_create("smack access test functions"); + tcase_add_checked_fixture(tc, setup, teardown); + /* TODO: add tests that use actual client Smack labels */ + suite_add_tcase(s, tc); + + tc = tcase_create("smack libsecurity functions"); + tcase_add_test(tc, smack_access_check); + suite_add_tcase(s, tc); + } else { + buxton_log("Smack support not detected; skipping this test suite\n"); + } + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + putenv("BUXTON_CONF_FILE=" ABS_TOP_BUILDDIR "/test/test.conf"); + s = daemon_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_utils.c b/test/check_utils.c new file mode 100644 index 0000000..fda288a --- /dev/null +++ b/test/check_utils.c @@ -0,0 +1,38 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#include +#include + +#include "check_utils.h" + +void setup_socket_pair(int *client, int *server) +{ + int socks[2]; + fail_if(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), + "socketpair: %m"); + + *client = socks[0]; + *server = socks[1]; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/check_utils.h b/test/check_utils.h new file mode 100644 index 0000000..81e1ae9 --- /dev/null +++ b/test/check_utils.h @@ -0,0 +1,42 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * buxton is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +/** + * \file check_utils.h Internal header + * This file is used internally by buxton to provide functionality + * used for testing + */ + +#pragma once + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +/** + * Set up a socket pair + * @param client Client socket file descriptor + * @param server Server socket file descriptor + */ +void setup_socket_pair(int *client, int *server); + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/test/test-configurator.conf b/test/test-configurator.conf new file mode 100644 index 0000000..cd693d2 --- /dev/null +++ b/test/test-configurator.conf @@ -0,0 +1,55 @@ +# +# Test config for buxton configurator +# + +[Configuration] +ModuleDirectory=/shut/your/mouth +DatabasePath=/you/are/so/suck +SmackLoadFile=/smack/smack/smack +SocketPath=/hurp/durp/durp + +[base] +Type=System +Backend=gdbm +Description=Operating System configuration layer +Priority=0 +# This will end up being a file at @@DB_PATH@@/base.db + +[isp] +Type=System +Backend=gdbm +Description=ISP specific settings +Priority=1 +# This will end up being a file at @@DB_PATH@@/isp.db + +[temp] +Type=System +Backend=memory +Priority=99 +Description=A termporary layer for scratch settings and data +# This will not end up in any file + +[user] +Type=User +Backend=gdbm +Priority=1000 +Description=Per-user settings +# This will end up in @@DB_PATH@@/user-.db + +[test-gdbm] +Type=System +Backend=gdbm +Priority=5000 +Description="GDBM test db" + +[test-memory] +Type=System +Backend=memory +Priority=5001 +Description="Memory test db" + +[test-gdbm-user] +Type=User +Backend=gdbm +Priority=6000 +Description=GDBM test db for user diff --git a/test/test-fail.ini.in b/test/test-fail.ini.in new file mode 100644 index 0000000..d194594 --- /dev/null +++ b/test/test-fail.ini.in @@ -0,0 +1,10 @@ +# +# All of these should trigger syntax errors +# +[default] +fail +boat +fail \ +boat +50 - 8 ; +x + y ; diff --git a/test/test-pass.ini.in b/test/test-pass.ini.in new file mode 100644 index 0000000..3c364dc --- /dev/null +++ b/test/test-pass.ini.in @@ -0,0 +1,26 @@ +# +# This is an example of a layer config file +# + +[Defaults] +Layers = OS, Platform, System, Custom, User; +SmackLabel= "_"; +Backend = gdbm; + +[OS] +Description = "Operating System configuration layer"; + +[Platform] +Description = "Platform configuration layer"; +Backend = memory; + +[System] +Description = "System configuration layer"; + +[Custom] +Description = "Special configuration layer"; +SmackLabel= "CarrierLocked"; + +[User] +Description = "Per User configuration layer"; + diff --git a/test/test.conf.in b/test/test.conf.in new file mode 100644 index 0000000..ac26e35 --- /dev/null +++ b/test/test.conf.in @@ -0,0 +1,55 @@ +# +# Default buxton config file +# + +[Configuration] +ModuleDirectory=@abs_top_builddir@/.libs +DatabasePath=@abs_top_builddir@/test/databases +SmackLoadFile=@abs_top_srcdir@/test/test.load2 +SocketPath=@abs_top_builddir@/test/buxton-socket + +[base] +Type=System +Backend=gdbm +Description=Operating System configuration layer +Priority=0 +# This will end up being a file at @@DB_PATH@@/base.db + +[isp] +Type=System +Backend=gdbm +Description=ISP specific settings +Priority=1 +# This will end up being a file at @@DB_PATH@@/isp.db + +[temp] +Type=System +Backend=memory +Priority=99 +Description=A termporary layer for scratch settings and data +# This will not end up in any file + +[user] +Type=User +Backend=gdbm +Priority=1000 +Description=Per-user settings +# This will end up in @@DB_PATH@@/user-.db + +[test-gdbm] +Type=System +Backend=gdbm +Priority=5000 +Description="GDBM test db" + +[test-memory] +Type=System +Backend=memory +Priority=5001 +Description="Memory test db" + +[test-gdbm-user] +Type=User +Backend=gdbm +Priority=6000 +Description=GDBM test db for user diff --git a/test/test.load2 b/test/test.load2 new file mode 100644 index 0000000..afea080 --- /dev/null +++ b/test/test.load2 @@ -0,0 +1,13 @@ +_ base/sample/key rw +_ system/sample/key rw +_ user/sample/key r +system base/sample/key r +system system/sample/key rw +system user/sample/key r +user base/sample/key r +user system/sample/key r +user user/sample/key rw +_ System rw +_ User rw +System _ rw +User _ rw -- 2.7.4