From: jk7744.park Date: Sat, 31 Jan 2015 07:11:56 +0000 (+0900) Subject: tizen 2.3 release X-Git-Tag: submit/tizen_2.3/20150202.054433 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Ftags%2Fsubmit%2Ftizen_2.3%2F20150202.054433;p=external%2Fbuxton.git tizen 2.3 release --- 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